notify()-notifyAll()-wait()-join()-yield()-sleep()

一、wait(),notify(),notifyAll()的理解与使用

 

这三个方法由于需要控制对对象的控制权(monitor),所以属于Object而不是属于线程。

wait(),会把持有该对象线程的对象控制权交出去,然后处于等待状态。

notify(),会通知某个正在等待这个对象的控制权的线程可以继续运行。

nofifyAll(),会通知所有等待这个对象控制权的线程继续运行,如果有多个正在等待该对象控制权时,具体唤醒哪个线程,就由操作系统进行调度。

下面我们先看一个例子:

package com.youyou.ch1.wn;

/**
 *类说明:快递实体类
 */
public class Express {
    public final static String CITY = "ShangHai";
    private int km;/*快递运输里程数*/
    private String site;/*快递到达地点*/

    public Express() {
    }

    public Express(int km, String site) {
        this.km = km;
        this.site = site;
    }

    /* 变化公里数,然后通知处于wait状态并需要处理公里数的线程进行业务处理*/
    public synchronized void changeKm(){
        this.km = 101;
        notify();
        //其他的业务代码

    }

    /* 变化地点,然后通知处于wait状态并需要处理地点的线程进行业务处理*/
    public synchronized void changeSite(){
        this.site = "BeiJing";
        notifyAll();
    }

    public synchronized void waitKm(){
        while(this.km<=100) {
            try {
                wait();
                System.out.println("check km thread["+Thread.currentThread().getId()
                        +"] is be notifed.");
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        System.out.println("the km is"+this.km+",I will change db.");

    }

    public synchronized void waitSite(){
        while(CITY.equals(this.site)) {
            try {
                wait();
                System.out.println("check site thread["+Thread.currentThread().getId()
                        +"] is be notifed.");
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        System.out.println("the site is"+this.site+",I will call user.");
    }
}
package com.youyou.ch1.wn;

/**
 *类说明:测试wait/notify/notifyAll
 */
public class TestWN {
    private static Express express = new Express(0,Express.CITY);

    /*检查里程数变化的线程,不满足条件,线程一直等待*/
    private static class CheckKm extends Thread{
        @Override
        public void run() {
            express.waitKm();
        }
    }

    /*检查地点变化的线程,不满足条件,线程一直等待*/
    private static class CheckSite extends Thread{
        @Override
        public void run() {
            express.waitSite();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        for(int i=0;i<3;i++){//三个线程
            new CheckSite().start();
        }
        for(int i=0;i<3;i++){//里程数的变化
            new CheckKm().start();
        }

        Thread.sleep(5000);
        express.changeKm();//快递地点变化
    }
}

结果总结:

1.在测试的时候,我们可以改变notify和notifyAll,看看结果是什么。结果会是完全不同的;

2.通过改变为notify的时候,我们发现的结果,我们知道,等待的线程,会被放到 队列里面,被唤醒的时候,就不太明确是哪个了?(由于是队列,那应该就是第一个)

我们来看另外的一个例子:

注意:

1.生产者,消费者必须要对同一份资源进行操作。

2.无论是执行对象的wait、notify还是notifyAll方法,必须保证当前运行的线程取得了该对象的控制权(monitor)

生产者:

package com.currentPro.waitAndnotify;

import java.util.ArrayList;
import java.util.List;

public class Producer implements Runnable{

    private List<Integer> taskQueue = new ArrayList<Integer>();
    
    //生产的量
    private final int MAX_CAPACITY;
    
    public Producer(List<Integer> sharedQueue,int size){
        this.taskQueue = sharedQueue; 
        this.MAX_CAPACITY = size;
    }
    
    @Override
       public void run()
       {
          int counter = 1;
          while (true)
          {
             try
             {
               synchronized (taskQueue)
                     {
                        while (taskQueue.size() == MAX_CAPACITY)
                        {
                           System.out.println("Queue is full " + Thread.currentThread().getName() + " is waiting , size: " + taskQueue.size());
                           taskQueue.wait();
                        }
                        Thread.sleep(1000);
                        taskQueue.add(counter);
                        System.out.println("Produced: " + counter);
                        counter++;
                        //唤醒正在等待的消费者,但是消费者是不是能获取到资源,由系统调度。
                        taskQueue.notifyAll();
                     }
             } 
             catch (InterruptedException ex)
             {
                ex.printStackTrace();
             }
          }
       }
    
}

消费者:

package com.currentPro.waitAndnotify;

import java.util.List;

public class Consumer implements Runnable{

    private final List<Integer> taskQueue ;
    
    
    public Consumer(List<Integer> sharedQueue){
        this.taskQueue = sharedQueue; 
    }
    
    @Override
       public void run()
       {
          while (true)
          {
             try
             {
                 synchronized (taskQueue)
                     {
                        while (taskQueue.isEmpty())
                        {
                           System.out.println("Queue is empty " + Thread.currentThread().getName() + " is waiting , size: " + taskQueue.size());
                           taskQueue.wait();
                        }
                        Thread.sleep(1000);
                        int i = (Integer) taskQueue.remove(0);
                        System.out.println("Consumed: " + i);
                        taskQueue.notifyAll();
                     }
             } catch (InterruptedException ex)
             {
                ex.printStackTrace();
             }
          }
       }
}

测试代码:

package com.currentPro.waitAndnotify;

import java.util.ArrayList;
import java.util.List;

public class Test {

    
    
    public static void main(String[] args) throws InterruptedException {
        //共享资源
        List<Integer> taskQueue = new ArrayList<Integer>();
        
        int MAX_CAPACITY = 5;
        //创建生产者线程
        Thread producer = new Thread(new Producer(taskQueue,MAX_CAPACITY),"producer");
        
        //创建消费者线程
        Thread consumer = new Thread(new Consumer(taskQueue),"consumer");
        
        consumer.start();
        Thread.sleep(2000);
        producer.start();
        
    }
}

二、join()-方法详解

现在我们还是认为,多个相同的线程是抢占式的,但是,我们有没有方法,让线程A,一定在线程B 之前先执行呢?

这里的话,我们可以暂时知道有两个方法,一个是 join() , 另外一个是 CountDownLatch。

下面我们先来讲述一下 join 方法。

下面我们来看一个简单的例子:

package com.youyou.ch1.demo1;

public class UseJoin {
    static class JumpQueue implements Runnable{
        private Thread thread;
        public JumpQueue(Thread thread){
            this.thread = thread;
        }

        public void run(){
            try {
                System.out.println(thread.getName()+" will be join before "
                        +Thread.currentThread().getName());
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+" terminted.");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread previous = Thread.currentThread();//现在是主线程
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(new JumpQueue(previous),String.valueOf(i));
            System.out.println(previous.getName()+" jump a queue the thread:"
                    +thread.getName());
            thread.start();
            previous = thread;
        }
        Thread.sleep(2000);//让主线程休眠2秒
        System.out.println(Thread.currentThread().getName() + " terminate.");
    }
}

下面我们在来一个简单的总结:

面试点

线程在执行yield()以后持有的锁是不释放的

sleep()方法被调用以后,持有的锁是不释放的

调动方法之前必须要持有锁调用了wait()方法以后,锁就会被释放,当wait方法返回的时候,线程会重新持有锁

调动方法之前必须要持有锁,调用notify()方法本身不会释放锁的

原文地址:https://www.cnblogs.com/lys-lyy/p/10958107.html