java多线程之wait notify详解,start于run区别,wait与sleep区别一篇通,附例:生产者消费者。

来源:互联网 发布:高考语文 知乎 编辑:程序博客网 时间:2024/06/07 15:10

一、wait(), notify(), notifyAll()等方法介绍1.wait()的作用是让当前线程进入等待状态,同时,wait()也会让当前线程释放它所持有的锁。“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法”,当前线程被唤醒(进入“就绪状态”)2.notify()和notifyAll()的作用,则是唤醒当前对象上的等待线程;notify()是唤醒单个线程,而notifyAll()是唤醒所有的线程。3.wait(long timeout)让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的notify()方法或 notifyAll() 方法,或者超过指定的时间量”,当前线程被唤醒(进入“就绪状态”)。注意:就绪状态未必会执行,要与其他线程竞争锁。知道获取到锁才会执行。

 wait是指在一个已经进入了同步锁的线程内,让自己暂时让出同步锁,而本线程失去锁以后,即刻停止运行。当再次被唤醒以后,从wait()处继续执行。以便其他正在等待此锁的线程可以得到同步锁并运行, 只有其他线程调用了notify方法(notify并不释放锁,只是告诉调用过wait方法的线程可以去参与获得锁的竞争了,但不是马上得到锁,因为锁还 在别人手里,别人还没释放。如果notify/notifyAll方法后面的代码还有很多,需要这些代码执行完后才会释放锁),调用wait方法的一个或多个线程就会解除wait状态,重新参与竞争对象锁,程序如果可以再次得到锁,就可以继续向下运行。

 

   1)wait()、notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写。

 

 2)当前线程必须拥有此对象的monitor(即锁),才能调用某个对象的wait()方法能让当前线程阻塞,

       (这种阻塞是通过提前释放synchronized锁,重新去请求锁导致的阻塞,这种请求必须有其他线程通过notify()或者notifyAll()唤醒重新竞争获得锁

 

 3)调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程;

      (notify()或者notifyAll()方法并不是真正释放锁,必须等到synchronized方法或者语法块执行完才真正释放锁

 

 4)调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程,唤醒的线程获得锁的概率是随机的,取决于cpu调度

二、Thread下的start与run的区别1. 用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法 run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。2.run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。这两个方法应该都比较熟悉,把需要并行处理的代码放在run()方法中,start()方法启动线程将自动调用 run()方法,这是由jvm的内存机制规定的。并且run()方法必须是public访问权限,返回值类型为void。
三、关于IllegalMonitorStateException异常(为什么执行wait、notify之前要加锁):抛出的异常表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程。也就是当前的线程不是此对象监视器的所有者。也就是要在当前线程锁定对象,才能用锁定的对象此行这些方法,需要用到synchronized ,锁定什么对象就用什么对象来执行notify(), notifyAll(),wait(), wait(long), wait(long, int)操作,否则就会报IllegalMonitorStateException异常。
四、wait与sleep的区别1.首先,要记住这个差别,“sleep是Thread类的方法,wait是Object类中定义的方法”。尽管这两个方法都会影响线程的执行行为,但是本质上是有区别的。2.Thread.sleep不会导致锁行为的改变,如果当前线程是拥有锁的,那么Thread.sleep不会让线程释放锁。如果能够帮助你记忆的话,可以简单认为和锁相关的方法都定义在Object类中,因此调用Thread.sleep是不会影响锁的相关行为。3.Thread.sleep和Object.wait都会暂停当前的线程,对于CPU资源来说,不管是哪种方式暂停的线程,都表示它暂时不再需要CPU的执行时间。OS会将执行时间分配给其它线程。区别是,调用wait后,需要别的线程执行notify/notifyAll才能够重新获得CPU执行时间。4.线程的状态参考 Thread.State的定义。新创建的但是没有执行(还没有调用start())的线程处于“就绪”,或者说Thread.State.NEW状态。Thread.State.BLOCKED(阻塞)表示线程正在获取锁时,因为锁不能获取到而被迫暂停执行下面的指令,一直等到这个锁被别的线程释放。BLOCKED状态下线程,OS调度机制需要决定下一个能够获取锁的线程是哪个,这种情况下,就是产生锁的争用,无论如何这都是很耗时的操作。
示例:
public class ThreadTest {    public static void main(String [] args){        Vector<Integer> obj = new Vector<Integer>();//线程安全的,相比于Arraylist速度较慢        Thread consumer = new Thread(new Consumer(obj));//创建消费者        Thread producter = new Thread(new Producter(obj));//创建生产者        consumer.start();//创建一个新的线程启动消费者。run()只是一个普通方法,如果用run()去执行,则和调用普通方法一样。依然是在主线程中执行。        producter.start();//创建一个新的线程启动生产者。    }}
//也可以使用下面的方式来创建并启动新的线程。
public class ThreadTest {    public static void main(String [] args){        Vector<Integer> obj = new Vector<Integer>();        ExecutorService service = Executors.newFixedThreadPool(2);        service.execute(new Consumer(obj)); //execute()没有返回值。入参只能是Runnable。        Future<Integer> rr2 = service.submit(new Producter(obj));//submit() 可以获取返回值。 如果需要返回值,则入参必须是Callable。    }}
public class Consumer implements Runnable {    private final Vector<Integer> obj;    public Consumer(Vector<Integer> obj) {        this.obj = obj;    }    @Override    public void run() {        synchronized (obj) {   //所有的锁操作都是Object的方法,而不是Thread的方法。            while (true) {                if (obj.isEmpty()) {                    try {                        obj.wait();//释放锁,等待其他线程唤醒,唤醒之后进入“就绪状态”                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }                System.out.println("Consumer:goods have been taken");                for (Integer ii : obj) {                    System.out.println("obj : " + ii);                }                obj.clear();                obj.notify();//所有的锁操作都是obj的操作,包括wait和notify。线程不会改变锁的状态            }        }    }}

public class Producter implements Runnable {    private Vector<Integer> obj;    public Producter(Vector<Integer> obj) {        this.obj = obj;    }    @Override    public void run() {        int i = 0;        synchronized (obj) {            while (true) {                if (!obj.isEmpty()) {                    try {                        obj.wait();                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                } else {                    obj.add(i++);                    obj.add(i++);                    obj.add(i++);                    obj.notify();                    System.out.println("Producter is ready");                    try {                        Thread.sleep(1000L);// 当前线程持锁等待。线程Thread里的方法无法改变锁状态。                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }        }    }}



 
阅读全文
0 0
原创粉丝点击