Java 并发编程二 (线程间的通信)

 

 简单通信

场景---四个线程:

分别为A,B,C,D,其中A,C线程做+1操作,B,D线程做-1操作,想要的结尾应该是A,C线程输出值为1,B,D线程输出值为0(要求 用线程间通信)

synchronized 实现

关键字 synchronized 与 wait()/notify() 这两个方法一起使用可以实现等待/通知模式

操作线程的时候,等待线程使用wait()
通知另外的线程操作用notify()、notifyAll()

假设有两个线程,该线程在执行过程中,判断值(不是该值等待,让其他线程抢),操作值,通知另外一个线程的调度

在线程等待和唤醒时,有一个非常重要的点:

查找 JDK1.8 文档,在 Object 的 wait() 方法中有如下介绍在一个参数版本中,中断和虚假唤醒是可能的,并且该方法应该始终在 循环 中使用

 所谓虚假唤醒,就是 wait()方法的一个特点,总结来说 wait() 方法使线程在哪里睡就在哪里醒

 

 在这种情况下,当 A 进入临界区,BCD三个线程在 if 判断后进入 wait() 等待,当A线程完成操作,此时 number 值为1,notifyAll() 会随机唤醒一个线程。

现在C被唤醒,由于 wait() 方法使线程在哪里睡就在哪里醒,所以接下来C在执行时不会再通过 if 判断而是直接+1,此时 number 就是2。从而导致和我们预想的不一致

正确实现代码:
 

// 创建一个资源类
class Share{
    // 设置临界资源
    private int number = 0;

    // 实现+1操作
    public synchronized void incr() throws InterruptedException {
        // 操作:判断、干活、通知
        if (number != 0) {
            // number不为0,等待
            this.wait();
        }
        number++;
        System.out.print(Thread.currentThread().getName()+"::"+number+"--->");
        // 唤醒其他线程
        this.notifyAll();
    }

    // 实现-1操作
    public synchronized void decr() throws InterruptedException {
        // 操作:判断、干活、通知
        if (number != 1) {
            // number不为0,等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"::"+number);
        this.notifyAll();
    }
}
public class InterThreadCommunication {
    public static void main(String[] args) {
        Share share = new Share();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    for (int i = 0; i < 100; i++) {
                        share.incr();
                    }

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    for (int i = 0; i < 100; i++) {
                        share.decr();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    for (int i = 0; i < 100; i++) {
                        share.incr();
                    }

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();

            new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    for (int i = 0; i < 100; i++) {
                        share.decr();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }
}

Lock 实现

关键字 synchronized 与 wait()/notify()这两个方法一起使用可以实现等待/通 知模式

Lock 锁的 newContition()方法返回 Condition 对象,Condition 类 也可以实现等待/通知模式。

用 notify()通知时,JVM 会随机唤醒某个等待的线程, 使用 Condition 类可以 进行选择性通知, Condition 比较常用的两个方法:

  • await()会使当前线程等待,同时会释放锁,当其他线程调用 signal()时,线程会重 新获得锁并继续执行。
  • signal()用于唤醒一个等待的线程。

==注意:在调用 Condition 的 await()/signal()方法前,也需要线程持有相关 的 Lock 锁,调用 await()后线程会释放这个锁,在 singal()调用后会从当前 Condition 对象的等待队列中,唤醒 一个线程,唤醒的线程尝试获得锁, 一旦 获得锁成功就继续执行。==

实例代码如下,主要改动的是资源类,main方法中代码不变

class Share {
    // 设置临界资源
    private int number = 0;
    // 创建一个Com
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    // 实现+1操作
    public void incr() {
        // 上锁
        lock.lock();
        try {
            // 判断
            while (number != 0) {
                condition.await();
            }
            // 干活
            number++;
            System.out.print(Thread.currentThread().getName() + "::" + number + "--->");
            // 通知
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    // 实现-1操作
    public void decr() throws InterruptedException {
        // 上锁
        lock.lock();
        try {
            // 判断
            while (number != 1) {
                condition.await();
            }
            // 干活
            number--;
            System.out.println(Thread.currentThread().getName() + "::" + number);
            // 通知
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

定制化线程通信

案列:启动三个线程,按照如下要求:
AA打印5此,BB打印10次,CC打印15次,一共进行10轮

具体思路:
每个线程添加一个标志位,是该标志位则执行操作,并且修改为下一个标志位,通知下一个标志 位的线程

创建一个可重入锁 private Lock lock = new ReentrantLock();
分别创建三个开锁通知 private Condition c1 = lock.newCondition();(他们能实现指定唤醒

(注意)具体资源类中的A线程代码操作
上锁,(执行具体操作(判断、操作、通知),解锁)放于try、finally,具体代码如下
 

class Share{
    private int flag = 1;

    private Lock lock = new ReentrantLock();
    // 创建三个Comdition对象,为了定向唤醒相乘
    Condition c1 = lock.newCondition();
    Condition c2 = lock.newCondition();
    Condition c3 = lock.newCondition();

    public void Aprint(int loop) {
        //上锁
        lock.lock();
        try{
            // 判断
            while(flag!=1) {
                c1.await();
            }
            // 干活
            for (int i = 1; i <= 5; i++) {
                System.out.println(Thread.currentThread().getName() + " ::本次第" + i + "次打印,是第" + loop+ "次循环");
            }
            flag = 2; //修改标志位,定向唤醒 线程b
            // 唤醒
            c2.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 解锁
            lock.unlock();
        }
    }
    public void Bprint(int loop) {
        //上锁
        lock.lock();
        try{
            // 判断
            while(flag!=2) {
                c2.await();
            }
            // 干活
            for (int i = 1; i <= 10; i++) {
                System.out.println(Thread.currentThread().getName() + " ::本次第" + i + "次打印,是第" + loop+ "次循环");
            }
            flag = 3; //修改标志位,定向唤醒 线程b
            // 唤醒
            c3.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 解锁
            lock.unlock();
        }
    }

    public void Cprint(int loop) {
        //上锁
        lock.lock();
        try{
            // 判断
            while(flag!=3) {
                c3.await();
            }
            // 干活
            for (int i = 1; i <= 15; i++) {
                System.out.println(Thread.currentThread().getName() + " ::本次第" + i + "次打印,是第" + loop+ "次循环");
            }
            flag = 1; //修改标志位,定向唤醒 线程b
            // 唤醒
            c1.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 解锁
            lock.unlock();
        }
    }
}

public class CustomInterThreadCommunication {
    public static void main(String[] args) {
        Share share = new Share();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 10; i++) {
                    share.Aprint(i);
                }

            }
        },"A").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 10; i++) {
                    share.Bprint(i);
                }
            }
        },"B").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 100; i++) {
                    share.Cprint(i);
                }

            }
        },"C").start();
    }
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值