• java多线程同步,等待,唤醒


    notify()、notifyAll()、wait()属于java.lang.Object,java.lang.Thread也是Object,自然也有上述方法;

    sleep()、interrupt()、interrupted()、join()、yield()属于java.lang.Thread

    wait()方法表示,放弃当前对资源的占有权,等啊等啊,一直等到有人通知我,我才会运行后面的代码。
    notify()方法表示,当前的线程已经放弃对资源的占有,通知等待的线程来获得对资源的占有权,但是只有一个线程能够从wait状态中恢复,然后继续运行wait()后面的语句;
    notifyAll()方法表示,当前的线程已经放弃对资源的占有,通知所有的等待线程从wait()方法后的语句开始运行。

            Obj.wait(),与Obj.notify()必须要与synchronized(Obj)一起使用,也就是wait,与notify是针对已经获取了Obj锁进行操作,从语法角度来说就是Obj.wait(),Obj.notify必须在synchronized(Obj){...}语句块内。从功能上来说wait就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。直到有其它线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行。相应的notify()就是对对象锁的唤醒操作。但有一点需要注意的是notify()调用后,并不是马上就释放对象锁的,而是在相应的synchronized(){}语句块执行结束,自动释放锁后,JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行。这样就提供了在线程间同步、唤醒的操作。Thread.sleep()与Object.wait()二者都可以暂停当前线程,释放CPU控制权,主要的区别在于Object.wait()在释放CPU同时,释放了对象锁的控制。

            单单在概念上理解清楚了还不够,需要在实际的例子中进行测试才能更好的理解。对Object.wait(),Object.notify()的应用最经典的例子,应该是三线程打印ABC的问题了吧,这是一道比较经典的面试题,题目要求如下:

    建立三个线程,A线程打印10次A,B线程打印10次B,C线程打印10次C,要求线程同时运行,交替打印10次ABC。这个问题用Object的wait(),notify()就可以很方便的解决。代码如下:

    package thread;  
      
    public class MyThreadPrinterABC implements Runnable {  
      
        private String name;  
        private Object prev;  
        private Object self;  
      
        private MyThreadPrinterABC(String name, Object prev, Object self) {  
            this.name = name;  
            this.prev = prev;  
            this.self = self;  
        }  
      
        @Override  
        public void run() {  
            int count = 10;  
            while (count > 0) {  
                synchronized (prev) {  
                    synchronized (self) {  
                        System.out.print(name);  
                        count--;  
      
                        self.notify();  
                    }  
                    try {  
                        if (count > 0)  
                            prev.wait();  
                    } catch (InterruptedException e) {  
                        e.printStackTrace();  
                    }  
                }  
            }  
        }  
      
        public static void main(String[] args) throws Exception {  
            Object a = new Object();  
            Object b = new Object();  
            Object c = new Object();  
            MyThreadPrinterABC pa = new MyThreadPrinterABC("A", c, a);  
            MyThreadPrinterABC pb = new MyThreadPrinterABC("B", a, b);  
            MyThreadPrinterABC pc = new MyThreadPrinterABC("C", b, c);  
      
            new Thread(pa).start();  
            Thread.sleep(1);  
            new Thread(pb).start();  
            Thread.sleep(1);  
            new Thread(pc).start();  
            Thread.sleep(1);  
        }  
    } 
    

      

    下面继续说sleep()join()yield()

    sleep()使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。也就是说如果有synchronized同步快,其他线程仍然不能访问共享数据。注意该方法要捕捉异常。

    例如有两个线程同时执行(没有synchronized)一个线程优先级为MAX_PRIORITY,另一个为MIN_PRIORITY,如果没有Sleep()方法,只有高优先级的线程执行完毕后,低优先级的线程才能够执行;但是高优先级的线程sleep(500)后,低优先级就有机会执行了。

    总之,sleep()可以使低优先级的线程得到执行的机会,当然也可以让同优先级、高优先级的线程有执行的机会。

    join()方法使调用该方法的线程在此之前执行完毕,也就是等待该方法的线程执行完毕后再往下继续执行。注意该方法也需要捕捉异常。

    yield()方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会。

    A.join,在API中的解释是,堵塞当前线程B,直到A执行完毕并死掉,再执行B;A.yield,A让出位置,给B执行,B执行结束A再执行。跟join意思正好相反!

    sleep 方法使当前运行中的线程睡眠一段时间,进入不可以运行状态,这段时间的长短是由程序设定的,yield方法使当前线程让出CPU占有权,但让出的时间是不可设定的。

    yield()也不会释放锁标志。

    实际上,yield()方法对应了如下操作;先检测当前是否有相同优先级的线程处于同可运行状态,如有,则把CPU的占有权交给次线程,否则继续运行原来的线程,所以yield()方法称为“退让”,它把运行机会让给了同等级的其他线程。

    sleep 方法允许较低优先级的线程获得运行机会,但yield()方法执行时,当前线程仍处在可运行状态,所以不可能让出较低优先级的线程此时获取CPU占有权。在一个运行系统中,如果较高优先级的线程没有调用sleep方法,也没有受到I/O阻塞,那么较低优先级线程只能等待所有较高优先级的线程运行结束,方可有机会运行。

    yield()只是使当前线程重新回到可执行状态,所有执行yield()的线程有可能在进入到可执行状态后马上又被执行,所以yield()方法只能使同优先级的线程有执行的机会。

    最后说一下interrupt和interrupted,直接看例子:

    package thread;  
      
    public class TestInterrupt implements Runnable {  
      
        int i = 10;  
      
        @Override  
        public void run() {  
            while (i-- > 0) {  
                System.out.println("I am running!");  
                try {  
                    Thread.sleep(100);  
                } catch (InterruptedException e) {  
                    System.out.println("--------- InterruptedException ----------");  
                    continue;  
                }  
            }  
        }  
      
        public static void main(String[] args) throws InterruptedException {  
            Thread t = new Thread(new TestInterrupt());  
            t.start();  
      
            Thread.sleep(500);// 运行一断时间后中断线程  
            System.out.println("****************************");  
            System.out.println("Interrupted Thread!");  
            System.out.println("****************************");  
      
            t.interrupt();//你以为到这里会中断吗?  
        }  
    }  
    I am running!  
    I am running!  
    I am running!  
    I am running!  
    I am running!  
    I am running!  
    ****************************  
    Interrupted Thread!  
    ****************************  
    --------- InterruptedException ----------  
    I am running!  
    I am running!  
    I am running!  
    I am running!  
    

      虽然中断发生了,但线程仍然在进行,离开线程有两种常用的方法:
    抛出InterruptedException和用Thread.interrupted()检查是否发生中断,下面分别看一下这两种方法:
    1.在阻塞操作时如Thread.sleep()时被中断会抛出InterruptedException(注意,进行不能中断的IO操作而阻塞和要获得对象的锁调用对象的synchronized方法而阻塞时不会抛出InterruptedException),代码如下:

    public void run() {    
            //死循环执行打印"I am running!"   
            try {    
                while (true) {    
                    System.out.println("I am running!");    
                        
                    //休眠一断时间,中断时会抛出InterruptedException    
                    Thread.sleep(50);    
                }    
            } catch (InterruptedException e) {    
                System.out.println("TestInterrupt.run() interrupted!");    
            }    
        }

    Thread.interrupted()检查是否发生中断.Thread.interrupted()能告诉你线程是否发生中断,并将清除中断状态标记,所以程序不会两次通知你线程发生了中断.代码如下:

    public void run() {    
               
           //检查程序是否发生中断    
           while (!Thread.interrupted()) {    
               System.out.println("I am running!");    
       
           }    
       
           System.out.println("TestInterrupt.run() interrupted!");    
       }    
  • 相关阅读:
    分布式解决方案的收集
    一天带你入门到放弃vue.js(三)
    一天带你入门到放弃vue.js(二)
    一天带你入门到放弃vue.js(一)
    JDK配置环境变量不成功的原因
    蚂蚁课堂(每特学院)-2期
    Java 使用blob对H5视频播放进行加密《java视频加密》
    Java 实现视频下载功能
    高并发与高可用实战之基础知识大型网站架构特征(一)
    Java线程池实现原理之自定义线程池(一)
  • 原文地址:https://www.cnblogs.com/zyl2016/p/6117427.html
Copyright © 2020-2023  润新知