ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

【JUC并发编程04】线程间定制化通信(单标志法存在的问题)

2022-01-31 17:03:57  阅读:148  来源: 互联网

标签:JUC 04 int lock flag 临界 线程 进程


文章目录

4 线程间定制化通信

案例实现

案列:启动三个线程,按照如下要求:
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();
    }
}

测试结果如下:

在这里插入图片描述

该案例需要注意

我们在学习操作系统中的同步可以知道,进程/线程同步有四个原则,都是为了禁止两个进程同时进入临界区。同步机制应该遵循以下原则

  • 空闲让进:临界区空闲时,可以允许一个请求进入临界区的进程立即进入临界区
  • 忙则等待:当已经有进程进入临界区的时候,其他试图进入临界区的进程必须等待
  • 有限等待:对请求访问的进程,应保证能在有限时间内进入临界区
  • 让权等待:当进程不能进入临界区的时候,应立即释放处理机,防止进程忙等待

很显然,该案例被称为单标志法。因为该案例设置一个公用整型变量flag,用于指示被允许进入临界区的进程编号。

若 flag =1,则允许 P 1 P_1 P1​ 进程进入临界区;若 flag =2,则允许 P 2 P_2 P2​ 进程进入临界区;若 flag =3,则允许 P 3 P_3 P3​ 进程进入临界区

该算法可确保每次只允许一个进程进入临界区。

但两个进程必须交替进入临界区,若某个进程不再进入临界区,则另一个进程也无法进入临界区

比如,若 P 3 P_3 P3​ 顺利进入临界区并从临界区离开,则此时临界区是空闲的,但 P 1 P_1 P1​ 并没有进入临界区的打算,flag = 1 一直成立, P 3 P_3 P3​ 就无法再次进入临界区。

违背了"空闲让进"原则,让资源利用不充分·

比如,将上述代码中的 main() 方法的C线程从10 改为 20 ,C线程不能访问 Share 资源了,因为 A 线程已经不再访问同时 flag 值不再改变了。

单标志法伪代码如下

//P_0进程
while(turn!=0);
critical section;
turn=1;
remainder section;
//P_1进程
while(turn!=1);
critical section;
turn=0;
remainder section;

标签:JUC,04,int,lock,flag,临界,线程,进程
来源: https://blog.csdn.net/xt199711/article/details/122760632

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有