wait() notify/notifyAll()的区别及运用

来源:互联网 发布:淘宝链接怎么发给客服 编辑:程序博客网 时间:2024/05/16 06:51
 

多线程wait( ),notify( ),notifyAll( )

     先来谈谈为什么所有的类中都有这一对方法,看是很奇怪,其实是 Thread类提供的,但是这一对却直接隶属于 Object 类,也就是说,所有对象都拥有这一对方法。因为这一对方法阻塞时要释放占用的锁,而锁是任何对象都具有的,调用任意对象的 wait() 方法导致线程阻塞,并且该对象上的锁被释放。
    notify()是释放对象的wait()方法而阻塞线程(但是也要当得到锁后才可以运行)但是这个释放是随机的,也就是不一定要释放那个线程。(因为调用同一资源的可能不是一个线程或者说是有多个阻塞的线程在等待,但是如果加了synchronized也只有一个线程,也有其他的线程在等待中,也就是阻塞)我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问题。
    除了 notify(),还有一个方法 notifyAll() 也可起到类似作用,唯一的区别在于,调用 notifyAll() 方法将把因调用该对象的 wait() 方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可执行状态。
    但是这一对方法却必须在 synchronized 方法或块中调用,理由也很简单,只有在synchronized 方法或块中当前线程才占有锁,才有锁可以释放。
    同样的道理,调用这一对方法的对象上的锁必须为当前线程所拥有,这样才有锁可以释放。因此,这一对方法调用必须放置在这样的 synchronized 方法或块中,该方法或块的上锁对象就是调用这一对方法的对象。若不满足这一条件,则程序虽然仍能编译,但在运行时会出现 IllegalMonitorStateException 异常。
    谈到阻塞,就不能不谈一谈死锁,略一分析就能发现,suspend() 方法和不指定超时期限的 wait() 方法的调用都可能产生死锁。遗憾的是,Java 并不在语言级别上支持死锁的避免,我们在编程中必须小心地避免死锁。

/**

 * 计算线程

 * 

 * @author leizhimin 2008-9-20 11:15:46

 */

class Calculator extends Thread {

int total;

public void run() {

synchronized (this) {

for (int i = 0; i < 101; i++) {

total += i;

}

System.out.println("notifyall....");

notifyAll();

System.out.println("notifyall.ededed...");

}

// 通知所有在此对象上等待的线程

}

}

/**

 * 获取计算结果并输出

 * 

 * @author leizhimin 2008-9-20 11:15:22

 */

public class ReaderResult extends Thread {

Calculator c;

public ReaderResult(Calculator c) {

this.c = c;

}

public void run() {

synchronized (c) {

try {

System.out.println(Thread.currentThread() + "等待计算结果。。。");

c.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(Thread.currentThread() + "计算结果为:" + c.total);

}

}

public static void main(String[] args) {

Calculator calculator = new Calculator();

// 启动三个线程,分别获取计算结果

new ReaderResult(calculator).start();

new ReaderResult(calculator).start();

new ReaderResult(calculator).start();

// 启动计算线程

calculator.start();

}

}

/** 

计算输出其他线程锁计算的数据 

*/ 

class ThreadA { 

    public static void main(String[] args) { 

        ThreadB b = new ThreadB(); 

        //启动计算线程 

        b.start(); 

        //线程A拥有b对象上的锁。线程为了调用wait()notify()方法,该线程必须是那个对象锁的拥有者 

        synchronized (b) { 

            try { 

            

                System.out.println("等待对象b完成计算。。。"); 

                //当前线程A等待 

                b.wait(); //这里并不是让bwait,是b的所有者线程wait,但是这个线程的苏醒的条件是bnotifynotifyAll方法的调用而且

            } catch (InterruptedException e) { 

                e.printStackTrace(); 

            } 

            System.out.println("b对象计算的总和是:" + b.total); 

        } 

    } 

}

 

/** 

计算1+2+3 ... +100的和 

* @author leizhimin 2008-9-15 13:20:49 

*/ 

class ThreadB extends Thread { 

    int total; 

    public void run() {

     System.out.println("xxxxxxxxxxxxxxxxxrrrrrrrrrrrrrrrrrrrrrx");

        synchronized (this) { 

         System.out.println("xxxxxxxxxxxxxxxxxx");

            for (int i = 0; i < 101; i++) { 

                total += i; 

            } 

            //(完成计算了)唤醒在此对象监视器上等待的单个线程,在本例中线程A被唤醒 

            notify(); 

        } 

    } 

}

class cakeStack {

private int value = 0; // 堆栈指针指向栈底 表示栈内没有cake

private int[] cakeBag = new int[10]; // 堆栈有10个字符的空间,定义cake栈的大小为10

public synchronized int get() { // 加锁

while (value == 0) { // 指针指向栈底,堆栈没有cake,没有数据可以出栈

try {

this.wait(); // 等待cooker线程把cake放入栈,child线程处于等待状态

} catch (InterruptedException e) {

}

}

this.notify(); // 解锁唤醒处于等待状态的线程

value--; // 指针向下移动

return cakeBag[value]; // 数据弹出栈,标号为valuecakechild线程取走

}

public synchronized void put(int c) { // 加锁

while (value == cakeBag.length) { // 栈满,不能压栈

try {

this.wait(); // 栈满,暂时cooker不生产cake,等待有child取蛋糕

} catch (InterruptedException e) {

}

}

this.notify();// 解锁

cakeBag[value] = c; // 数据入栈,cooker将生产到的标号为valuecake放入栈

value++; // 指针向上移动,栈中内容加1

}

}

class cooker implements Runnable { // cooker

cakeStack theStack; // cooker类生成的cake都放到同步堆栈中

public cooker(cakeStack s) {

theStack = s;

}

public void run() {

int c;

for (int i = 0;; i++) {// 生产随机次

c = (int) (Math.random() * 10); // 随机产生10个数字,表示不同标号的cake

theStack.put(c); // 把不同标号的cake入栈

System.out.println("cooker make: \t " + c);

try {

Thread.sleep((int) (Math.random() * 1000));

} catch (InterruptedException e) {

}

}

}

}

class child implements Runnable { // child

cakeStack theStack; // child类获得的字符都来自同步堆栈

public child(cakeStack s) {

theStack = s;

}

public void run() {

int c;

for (int i = 0;; i++) {

c = theStack.get(); // child类从堆栈中读取数据

System.out.println("child eat:\t" + c);

try {

Thread.sleep((int) (Math.random() * 1000));

} catch (InterruptedException e) {

}

}

}

}

public class StackTest {

public static void main(String args[]) {

cakeStack stack = new cakeStack();

Thread cooker = new Thread(new cooker(stack));

Thread child = new Thread(new child(stack));

System.out.println("Person\t\tcakeMark");

cooker.start();

child.start();

}

}