线程通信_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > 线程通信

线程通信

 2017/6/29 5:31:14  神绮_H_亚里亚  程序员俱乐部  我要评论(0)
  • 摘要:1.生产者与消费者生产者与消费者是个很好的线程通信的例子,生产者在一个循环中不断生产共享数据,而消费者则不断消费生产者生产的共享数据。程序必须保证有共享数据,如果没有,消费者必须等待生产新的共享数据。两者之间的数据关系如下:1)生产者生产前,如果共享数据没有被消费,则生产等待;生产者生产后,通知消费者消费。2)消费者消费前,如果共享数据已经被消费完,则消费者等待;消费者消费后,通知生产者生产。为了解决生产者和消费者的矛盾,引入了等待/通知(wait/notify)机制
  • 标签:线程

1. 生产者与消费者

?

? ? 生产者与消费者是个很好的线程通信的例子,生产者在一个循环中不断生产共享数据,而消费者则不断消费生产者生产的共享数据。程序必须保证有共享数据,如果没有,消费者必须等待生产新的共享数据。两者之间的数据关系如下:
1)
生产者生产前,如果共享数据没有被消费,则生产等待;生产者生产后,通知消费者消费。
2
)消费者消费前,如果共享数据已经被消费完,则消费者等待;消费者消费后,通知生产者生产。
? ?
为了解决生产者和消费者的矛盾,引入了等待/通知(wait/notify)机制。

?

class="java" name="code">class Producer extends Thread {  
    Queue q;  
  
    Producer(Queue q) {  
        this.q = q;  
    }  
    public void run() {  
        for (int i = 1; i < 5; i++) {  
            q.put(i);  
	        }  
	    }  
	}  
	  
	class Consumer extends Thread {  
	    Queue q; // 声明队列q  
	    Consumer(Queue q){   
	        this.q = q; // 队列q初始化  
	    }  
	    public void run() {  
	        while (true) {// 循环消费元素  
	            q.get(); // 获取队列中的元素  
	        }  
	    }  
	}  

??? Producer 是一个生产者类,该生产者类提供一个以共享队列作为参数的构造方法,它的run 方法循环产生新的元素,并将元素添加于共享队列;Consumer 是一个消费者类,该消费者类提供一个以共享队列作为参数的构造方法,它的 run 方法循环消费元素,并将元素从共享队列删除。?

?

2.共享队列?

共享队列类是用于保存生产者生产、消费者消费的共享数据。共享队列有两个域:value(元素的数目)、isEmpty(队列的状态)。共享队列提供了put get 两个方法。?

?

class Queue {  
    int value = 0; // 声明,并初始化整数类型数据域value  
    boolean isEmpty = true; // 声明,并初始化布尔类型数据域isEmpty,用于判断队列的状态  
  
    // 生产者生产方法  
    public synchronized void put(int v) {  
        // 如果共享数据没有被消费,则生产者等待  
        if (!isEmpty) {  
            try {  
	                System.out.println("生产者等待");  
	                wait(); // 进入等待状态  
	            } catch (Exception e) // 捕获异常  
	            {  
	                e.printStackTrace(); // 异常信息输出  
	            }  
	        }  
	        value += v; // value值加v  
	        isEmpty = false; // isEmpty赋值为false  
	        System.out.println("生产者共生产数量:" + v);  
	        notify();  
	    }  
	  
	    public synchronized int get() {  
	        if (isEmpty) {  
	            try {  
	                System.out.println("消费者等待");  
	                wait();  
	            } catch (Exception e) {  
	                e.printStackTrace();  
	            }  
	        }  
	        value--;  
	        if (value < 1) {  
	  
	            isEmpty = true;  
	        }  
	        System.out.println("消费者消费一个,剩余:" + value);  
	        notify();  
	        return value;  
	    }  
	}  

??? 生产者调用put方法生产共享数据,如果共享数据不为空,生产者线程进入等待状态;否则将生成新的数据,然后调用notify方法唤醒消费者线程进行消费;

?

消费者调用get方法消费共享数据,如果共享数据为空,消费者进入等待状态,否则将消费共享数据,然后提调用notify方法唤醒生产者线程进行生产。

3. 运行生产者与消费者

? 下面是生产者与消费者程序的主程序。

	public class ThreadCommunication {  
	    public static void main(String[] args) {  
	        Queue q = new Queue();  
	        Producer p = new Producer(q);  
	        Consumer c = new Consumer(q);  
	        c.start();  
	        p.start();  
	    }  
	}  

?

注意:考虑到程序的安全性,多数情况下使用 notifiAll(),除非明确可以知道唤醒哪一个线程。wait方法调用的前提条件是当前线程获取了这个对象的锁,也就是说 wait方法必须放在同步块或同步方法中。?

?

6. 线程死锁

? ? ? 为了保证数据安全使用 synchronized同步机制,当线程进入堵塞状态(不可运行状态和等待状态)时,其他线程无法访问那个加锁对象(除非同步锁被解除),所以

一个线程会一直处于等待另一个对象的状态,而另一个对象又会处于等待下一个对象的状态,以此类推,这个线程等待状态链会发生很糟糕的情形,即封闭环状态(也就是说最后那个对象在等待第一个对象的锁)。此时,所有的线程都陷入毫无止境的等待状态中,无法继续运行,这种情况就称为死锁。虽然这种情况发生的概率很小,一旦出现,程序的调试变得困难而且查错也是一件很麻烦的事情。

? ? 下面举一个死锁的例子。

public class ThreadLocked implements Runnable {  
    public static boolean flag = true; // 起一个标志作用  
    private static Object A = new Object(); // 声明,并初始化静态Object数据域A  
  
    private static Object B = new Object(); // 声明,并初始化静态Object数据域B  
  
    public static void main(String[] args) throws InterruptedException {  
        Runnable r1 = new ThreadLocked(); // 创建,并初始化ThreadLocked对象r1  
        Thread t1 = new Thread(r1); // 创建线程t1  
	        Runnable r2 = new ThreadLocked(); // 创建,并初始化ThreadLocked对象r2  
	        Thread t2 = new Thread(r2); // 创建线程t2  
	        t1.start(); // 启动线程t1  
	        t2.start(); // 启动线程t2  
	    }  
	  
	    public void AccessA() {  
	        flag = false; // 初始化域flag  
	        // 同步代码快  
	        synchronized (A) { // 声明同步块,给对象A加锁  
	            System.out.println("线程t1 : 我得到了A的锁"); // 输出字符串信息  
	            try {  
	                // 让当前线程睡眠,从而让另外一个线程可以先得到对象B的锁  
	                Thread.sleep(1000); // 休眠  
	            } catch (InterruptedException e) { // 捕获异常  
	                e.printStackTrace(); // 异常信息输出  
	            }  
	            System.out.println("线程t1 : 我还想要得到B的锁");  
	            // 在得到A锁之后,又想得到B的锁  
	            // 同步块内部嵌套同步块  
	            synchronized (B) { // 声明内部嵌套同步块,指定对象B的锁  
	                System.out.println("线程t1 : 我得到了B的锁"); // 输出字符串信息  
	            }  
	        }  
	    }  
	  
	    public void AccessB() {  
	        flag = true; // 修改flag的值  
	        // 同步代码块  
	        synchronized (B) { // 指定同步块,给B加锁  
	            System.out.println("线程t2 : 我得到了B的锁"); // 输出字符串信息  
	            try {  
	                // 让当前线程睡眠,从而让另外一个线程可以先得到对象A的锁  
	                Thread.sleep(1000); // 休眠  
	            } catch (InterruptedException e) { // 捕获异常InterruptedException  
	                e.printStackTrace(); // 异常信息输出  
	            }  
	            System.out.println("线程t2 : 我还想要得到A的锁"); // 字符串信息输出  
	            // 在得到B锁之后,又想得到A的锁  
	            // 同步块内部嵌套内部快  
	            synchronized (A) { // 指定同步块,给A加锁  
	                System.out.println("线程t2 : 我得到了A的锁"); // 输出字符串信息  
	            }  
	        }  
	    }  
	  
	    public void run() {  
	        if (flag){ // 当flag为true,执行下面语句  
	            AccessA(); // 调用AccessA方法  
	        } else {  
	            AccessB(); // 调用AccessB方法  
	        }  
	    }  
	  
	}  

?

??程序 ThreadLocked.java中创建了两个线程 t1 t2,并且声明两个方法:AccessA AccessB。在运行过程中,线程t1 先获得了 A 的锁,然后又要求获得 B 的锁;而 t2
先获得B 的锁,然后又要求获得 A的锁,此时便进入了无休止的相互等待状态,即死锁。?

?

Java 语言本身并没有提供防止死锁的具体方法,但是在具体程序设计时必须要谨慎,以防止出现死锁现象。通常在程序设计中应注意,不要使用 stop()suspend()resume()以及 destroy()方法。 stop()方法不安全,它会解除由该线程获得的所有对象锁,而且可能使对象处于不连贯状态,如果其他线程此时访问对象,而导致的错误很难检查出来。suspend()/resume ()方法也极不安全,调用 suspend()方法时,线程会停下来,但是该线程并没有放弃对象的锁,导致其他线程并不能获得对象锁。调用destroy()会强制终止线程,但是该线程也不会释放对象锁。?

?

发表评论
用户名: 匿名