线程间通信

线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体。线程间通信就是成为整体的必用方案之一。

一、等待/通知机制

    等待通知在现实中处处可见,比如就餐时,初始做好饭之前,服务员一直在等待,而饭一旦做好,通知服务员上餐,这就是等待/通知。

    等待通知的实现使用wait方法和notify方法实现。

    wait方法的作用使当前执行代码的线程等待,知道接到通知或被中断为止。在调用wait方法之前,线程必须获得该对象的对象级锁,即wait方法只能在同步方法或者同步代码块中调用。执行wait方法后,线程释放对象锁。使得其他等待对象锁的线程获得该对象锁。

    notify方法也要在同步方法或同步代码块中调用,即在调用notify之前,线程也必须获得该对象的对象锁。当执行notify方法后,当前线程不会立即释放对象锁,而是要等到notify方法的线程将程序执行完,也就是退出synchornized代码块后,当前线程才会释放对象锁,而呈wait状态的线程才可以获取该对象锁。

class FanXin{
    public static void main(String[] args) throws InterruptedException {
        Print p = new Print();
        Thread A = new Thread(){
            @Override
            public void run() {
                p.f1();
            }
        };
        A.setName("线程A");
        A.start();
        Thread.sleep(3000);
        Thread B = new Thread(){
            @Override
            public void run() {
                p.f2();
            }
        };
        B.setName("线程B");
        B.start();
    }
}
class Print{
    public void f1(){
        try {
            synchronized (this){
                System.out.println(Thread.currentThread().getName()+"开始");
                this.wait();
                System.out.println(Thread.currentThread().getName()+"结束");
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public void f2(){
        try {
            synchronized (this){
                System.out.println(Thread.currentThread().getName()+"开始");
                this.notify();
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName()+"结束");
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
如上代码,线程A执行f1方法,线程B执行f2方法,当线程A执行到wait方法时,其释放对象锁,并再次等待该对象的通知,所以它只会打印一行,如下图所示

    

当3秒过后,线程B启动,执行完notify方法后。刚才也说过了,线程执行notify方法后,不会立即释放对象锁,而是等线程退出synchronized代码块后才释放锁。便有了如下结果


可见,线程B在休眠期间,线程A并没有获得对象锁。当线程B退出synchronized代码块后,线程A获得该对象锁,继续执行未完的方法。结果如下

扫描二维码关注公众号,回复: 157320 查看本文章


刚才的例子只是唤醒一个线程,如果还有其他线程在等待,且唤醒的线程执行条件不足,而没有执行,现在所有的线程都在等待锁的释放,这个时候如果我们唤醒所有线程,让他们再次去竞争锁,这样发生死锁的概率就降低了。

notifyAll方法将所有线程都会唤醒,让他们重新去竞争锁。

我们将上面的代码做一点修改。修改后的代码如下

class FanXin{
    public static void main(String[] args) throws InterruptedException {
        Print p = new Print();
        Thread A = new Thread(){
            @Override
            public void run() {
                p.f1();
            }
        };
        A.setName("线程A");
        Thread B = new Thread(){
            @Override
            public void run() {
                p.f2();
            }
        };
        B.setName("线程B");
        Thread C = new Thread(){
            @Override
            public void run() {
                p.f3();
            }
        };
        C.setName("线程C");
        C.start();
        B.start();
        A.start();
    }
}
class Print{
    private int i = 1;
    public void f1(){
        try {
            synchronized (this){
                if (i!=1)
                    this.wait();
                System.out.println(Thread.currentThread().getName()+"开始");
                System.out.println(Thread.currentThread().getName()+"结束");
                this.notify();
                i=2;
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public void f2(){
        try {
            synchronized (this){
                if (i!=2)
                    this.wait();
                System.out.println(Thread.currentThread().getName()+"开始");
                System.out.println(Thread.currentThread().getName()+"结束");
                this.notify();
                i=3;
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public void f3(){
        try {
            synchronized (this){
                if (i!=3)
                    this.wait();
                System.out.println(Thread.currentThread().getName()+"开始");
                System.out.println(Thread.currentThread().getName()+"结束");
                this.notify();
                i=1;
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
可见,有三个线程,线程A对应执行f1,线程B执行f2,线程C执行f3。问题出现了,线程C先抢到CPU,因为条件不满足,所以线程C等待,此时线程A得到CPU资源执行完f1,而notify是随即唤醒一个线程,假如,唤醒了线程C,而线程C也执行完了f3,此时

i的值为1,线程B始终得到不运行,出现了死锁,如下图所示


要解决这个问题的死锁问题,线程条盘判断入手,因为if判断只有一次,如果这次条件不符合,线程会等待,但是当被唤醒之后,这个条件就没有用处了,我们就不确定是线程从等待处开始执行,还是从判断条件执行,所以我们将if改为while,让他被唤醒时也要判断线程是否满足条件。并且我们要唤醒更多的线程来重新竞争锁,这样可以避免上面死锁的问题。代码修改如下

class FanXin{
    public static void main(String[] args) throws InterruptedException {
        Print p = new Print();
        Thread A = new Thread(){
            @Override
            public void run() {
                p.f1();
            }
        };
        A.setName("线程A");
        Thread B = new Thread(){
            @Override
            public void run() {
                p.f2();
            }
        };
        B.setName("线程B");
        Thread C = new Thread(){
            @Override
            public void run() {
                p.f3();
            }
        };
        C.setName("线程C");
        C.start();
        B.start();
        A.start();
    }
}
class Print{
    private int i = 1;
    public void f1(){
        try {
            synchronized (this){
                    while (i != 1)
                        this.wait();
                    System.out.println(Thread.currentThread().getName() + "开始");
                    System.out.println(Thread.currentThread().getName() + "结束");
                    this.notifyAll();
                    i = 2;
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public void f2(){
        try {
            synchronized (this) {
                    while (i != 2)
                        this.wait();
                    System.out.println(Thread.currentThread().getName() + "开始");
                    System.out.println(Thread.currentThread().getName() + "结束");
                    this.notifyAll();
                    i = 3;
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public void f3(){
        try {
            synchronized (this){
                    while (i != 3)
                        this.wait();
                    System.out.println(Thread.currentThread().getName() + "开始");
                    System.out.println(Thread.currentThread().getName() + "结束");
                    this.notifyAll();
                    i = 1;
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
这样就可以避免死锁。

猜你喜欢

转载自blog.csdn.net/yanghan1222/article/details/80214282