sleep()方法


sleep是指让当前正在运行的线程休眠一段指定的时间,休眠的过程以及休眠结束继续运行都由当前线程自己控制。比如,我要吃一个苹果的流程是拿苹果-->洗苹果-->吃苹果。在我拿起苹果之后我突然想休息一会,在我休息了分钟之后,我再继续去执行洗苹果的动作,整个的动作执行由我这个线程控制。

 

 

wait()方法


wait也是让当前线程暂停(阻塞)一段时间,这个方法是由某个确定的对象来调用的。当这个对象在某个线程李说“暂停”,也就是this.wait();当前线程也就进入阻塞状态。就好比一个人监督着我要吃一个苹果的流程(拿苹果-->洗苹果-->吃苹果),在我洗完苹果后,他突然说暂停,这时我就进入暂停(阻塞)状态。必须等待他说继续也就是this.notify(),或者this.notifuAll()。然后我继续进行吃苹果的动作。

小结


其实两者都可以让线程暂停一段时间,但是本质的区别是一个线程的运行状态控制,一个是线程之间的通讯的问题

 在java.lang.Thread类中,提供了sleep(),
而java.lang.Object类中提供了wait(), notify()和notifyAll()方法来操作线程
sleep()可以将一个线程睡眠,参数可以指定一个时间。
而wait()可以将一个线程挂起,直到超时或者该线程被唤醒。
    wait有两种形式wait()和wait(milliseconds).
sleep和wait的区别有:
  1,这两个方法来自不同的类分别是Thread和Object
  2,最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
  3,wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在
    任何地方使用
   synchronized(x){
      x.notify()
     //或者wait()
   }
   4,sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常

 

notify()和notifyAll()


这两个方法都是唤醒想成,最大区别是notify()是唤醒线程池中最先(最先两个字是重点)加入的一个线程,而notifyAll()是唤醒线程池中所有的线程。

 

生产消费模式举例

 

创建苹果对象:

public class Apple {
    int id;
    public Apple(int id){
        this.id=id;
    }
    public String toString(){
        return "apple:"+id;
    }
}

生产消耗控制者:

public class AppleBox {
    int index = 0;
    Apple[] apples = new Apple[5];


    //生产苹果
    public synchronized void productApple(Apple apple) {
       while(index>=apples.length){
           try{
               this.wait();
           } catch (Exception e) {
               e.printStackTrace();
           }
       }
       this.notify();
       apples[index]=apple;
       System.out.println(Thread.currentThread().getName()+"生产编号为:"+apple.id+"的苹果");
       index++;
    }
    //消耗苹果
    public synchronized Apple eatApple(){
        while (index<=0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.notify();
        index--;
        System.out.println(Thread.currentThread().getName()+"消耗编号为:"+apples[index].id+"的苹果");
        return apples[index];
    }
}

生产者:

import java.util.Random;

public class Producer implements Runnable {
    private AppleBox box;

    public Producer(AppleBox box) {
        this.box = box;
    }

    @Override
    public void run() {
        Random rd = new Random();
        for (int i = 0; i < 20 ; i++) {
            Apple apple = new Apple(i+1);
            box.productApple(apple);
            System.out.println("生产了:"+apple.id+"苹果");
            try{
                Thread.sleep(rd.nextInt(500));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

消费者:

import java.util.Random;

public class Customer implements Runnable {
    private  AppleBox box;

    public Customer(AppleBox box) {
        this.box = box;
    }

    @Override
    public void run() {
        Random rd = new Random();
        for (int i = 0; i < 20; i++) {
            Apple apple = box.eatApple();
            System.out.println("消耗了:"+apple.id+"苹果");
            try{
                Thread.sleep(rd.nextInt(500));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

测试方法:

public class Test {

    public static void main(String[] args) {
        AppleBox box = new AppleBox();
        Producer producer = new Producer(box);

        Customer customer = new Customer(box);
        Customer customer1 = new Customer(box);
        new Thread(producer).start();
        new Thread(customer).start();
        new Thread(customer1).start();
    }
}

在控制方法中使用notify()唤醒方法,执行结果为:

Thread-0生产编号为:1的苹果
生产了:1苹果
Thread-1消耗编号为:1的苹果
消耗了:1苹果
Thread-0生产编号为:2的苹果
生产了:2苹果
Thread-2消耗编号为:2的苹果
消耗了:2苹果
Thread-0生产编号为:3的苹果
Thread-1消耗编号为:3的苹果
消耗了:3苹果
生产了:3苹果
Thread-0生产编号为:4的苹果
生产了:4苹果
Thread-2消耗编号为:4的苹果
消耗了:4苹果
Thread-0生产编号为:5的苹果
生产了:5苹果
Thread-1消耗编号为:5的苹果
消耗了:5苹果
Thread-0生产编号为:6的苹果
Thread-2消耗编号为:6的苹果
消耗了:6苹果
生产了:6苹果
Thread-0生产编号为:7的苹果
生产了:7苹果
Thread-1消耗编号为:7的苹果
消耗了:7苹果
Thread-0生产编号为:8的苹果
生产了:8苹果
Thread-2消耗编号为:8的苹果
消耗了:8苹果
Thread-0生产编号为:9的苹果
生产了:9苹果
Thread-1消耗编号为:9的苹果
消耗了:9苹果
Thread-0生产编号为:10的苹果
Thread-1消耗编号为:10的苹果
消耗了:10苹果
生产了:10苹果
Thread-0生产编号为:11的苹果
生产了:11苹果
Thread-2消耗编号为:11的苹果
消耗了:11苹果
Thread-0生产编号为:12的苹果
生产了:12苹果
Thread-1消耗编号为:12的苹果
消耗了:12苹果
Thread-0生产编号为:13的苹果
生产了:13苹果
Thread-2消耗编号为:13的苹果
消耗了:13苹果
Thread-0生产编号为:14的苹果
生产了:14苹果
Thread-2消耗编号为:14的苹果
消耗了:14苹果
Thread-0生产编号为:15的苹果
生产了:15苹果
Thread-1消耗编号为:15的苹果
消耗了:15苹果
Thread-0生产编号为:16的苹果
生产了:16苹果
Thread-2消耗编号为:16的苹果
消耗了:16苹果
Thread-0生产编号为:17的苹果
生产了:17苹果
Thread-1消耗编号为:17的苹果
消耗了:17苹果
Thread-0生产编号为:18的苹果
Thread-2消耗编号为:18的苹果
消耗了:18苹果
生产了:18苹果
Thread-0生产编号为:19的苹果
生产了:19苹果
Thread-2消耗编号为:19的苹果
消耗了:19苹果
Thread-0生产编号为:20的苹果
生产了:20苹果
Thread-1消耗编号为:20的苹果
消耗了:20苹果

当使用notifyAll()唤醒方法时,执行结果为:

Thread-0生产编号为:1的苹果
生产了:1苹果
Thread-1消耗编号为:1的苹果
消耗了:1苹果
Thread-0生产编号为:2的苹果
生产了:2苹果
Thread-1消耗编号为:2的苹果
消耗了:2苹果
Thread-0生产编号为:3的苹果
生产了:3苹果
Thread-1消耗编号为:3的苹果
消耗了:3苹果
Thread-0生产编号为:4的苹果
生产了:4苹果
Thread-2消耗编号为:4的苹果
消耗了:4苹果
Thread-0生产编号为:5的苹果
生产了:5苹果
Thread-2消耗编号为:5的苹果
消耗了:5苹果
Thread-0生产编号为:6的苹果
生产了:6苹果
Thread-1消耗编号为:6的苹果
消耗了:6苹果
Thread-0生产编号为:7的苹果
生产了:7苹果
Thread-2消耗编号为:7的苹果
消耗了:7苹果
Thread-0生产编号为:8的苹果
Thread-2消耗编号为:8的苹果
消耗了:8苹果
生产了:8苹果
Thread-0生产编号为:9的苹果
生产了:9苹果
Thread-2消耗编号为:9的苹果
消耗了:9苹果
Thread-0生产编号为:10的苹果
生产了:10苹果
Thread-2消耗编号为:10的苹果
消耗了:10苹果
Thread-0生产编号为:11的苹果
生产了:11苹果
Thread-1消耗编号为:11的苹果
消耗了:11苹果
Thread-0生产编号为:12的苹果
生产了:12苹果
Thread-2消耗编号为:12的苹果
消耗了:12苹果
Thread-0生产编号为:13的苹果
生产了:13苹果
Thread-1消耗编号为:13的苹果
消耗了:13苹果
Thread-0生产编号为:14的苹果
生产了:14苹果
Thread-2消耗编号为:14的苹果
消耗了:14苹果
Thread-0生产编号为:15的苹果
Thread-2消耗编号为:15的苹果
消耗了:15苹果
生产了:15苹果
Thread-0生产编号为:16的苹果
Thread-2消耗编号为:16的苹果
消耗了:16苹果
生产了:16苹果
Thread-0生产编号为:17的苹果
生产了:17苹果
Thread-2消耗编号为:17的苹果
消耗了:17苹果
Thread-0生产编号为:18的苹果
生产了:18苹果
Thread-1消耗编号为:18的苹果
消耗了:18苹果
Thread-0生产编号为:19的苹果
生产了:19苹果
Thread-2消耗编号为:19的苹果
消耗了:19苹果
Thread-0生产编号为:20的苹果
Thread-1消耗编号为:20的苹果
消耗了:20苹果
生产了:20苹果

可以看出第一次打印结果,消费者1和消费者2是轮流出现,这是由于两者被轮流唤醒导致;第二次打印结果,消费者1和消费者2是随机出现。这是由于两个线程同时被唤醒,然后争夺CPU资源所导致的。由此可以很明显的看出notify()和notifyAll()的区别。

后记:第一次写这种博客,有所不足请在评论中指出。谢谢。