博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ReentrantLock实现 多线程顺序执行任务
阅读量:6165 次
发布时间:2019-06-21

本文共 6244 字,大约阅读时间需要 20 分钟。

题目摘自:

最近看了这位博主的文章 写的挺好的 跟着里面的线程 温习了一遍 结尾处有道题算是复习巩固吧

我是用ReentrantLock实现的 而不是synchronized

题目:

使用3个线程,要求三个线程顺序执行,不允许使用sleep()强制让线程有顺序。线程A输出1线程B输出2线程C输出3线程A输出4线程B输出5以此类推,一直输出到1000为止。

题目是不是看着不难 一开始我也是这么觉得的

后来我发现自己错了 还错了很离谱…… 也许是自己思维不够灵活 花了一下午时间解决……
不过也算是初步掌握了 ReentrantLock的运用

讲一下思路吧

题目要求是ABC顺序依次输出结果

可线程是抢占式的,鬼会听你安排 一个一个输出哦 肯定都是抢着去占CPU

线程是并发式的   意思就是 看起来像是一起执行  实际是一次只能执行一次线程

那怎么才可以让线程乖乖听话?

答:一个执行的时候 另外两个等待不就OK了

比如 A执行的时候BC等待 A执行完后唤醒B执行 B执行完后唤醒C执行 C执行完后唤醒A执行 这样一直循环 就是ABCABC的执行顺序了

过程:

思路有了,那我们如何来实现呢?

一开始 脑子一蹦出来就是用synchronized来解决 后来想想不行啊

synchronized的唤醒方式有2种(学的不深 不知道是否还有其他方法)

1. notify() 随机唤醒一个线程
2. notifyAll() 唤醒全部线程

题目要求我们是按照顺序ABC来执行线程的 我滴个神啊 这tm不是断我路吗!

别急别急 还好有 ReentrantLock

题外话:

话说网上一直有争议 到底是ReentrantLock处理并发好还是synchronized好?

讲实话 我也很难说清楚 因为自己也是学的不精 不过大致可以这么理解:

在并发量比较小的情况下,使用synchronized是个不错的选择,但是在并发量比较高的情况下,其性能下降很严重,此时ReentrantLock是个不错的方案

回到刚才的问题:如何使用ReentrantLock来解决顺序执行的问题?

首先我们得创建ReentrantLock锁和Condition小黑屋

private ReentrantLock r = new ReentrantLock();  //创建锁    private Condition c1 = r.newCondition();    //小黑屋C1    private Condition c2 = r.newCondition();    //小黑屋C2    private Condition c3 = r.newCondition();    //小黑屋C3

上面小黑屋什么意思呢?

其实小黑屋是我给它取得名字 因为它很符合盒子的形象:

A执行的时候让BC等待  B,C就被关到c2,c3这两间小黑屋里  没有人来开门 它们就永远都无法出来(B,C等待唤醒)

第一条拦路虎:如何防止抢占?

多线程的常见问题 :A线程执行的时候B抢占CPU A停止手头工作等待 这会产生脏数据等问题

所以得加锁呀

private ReentrantLock r = new ReentrantLock();          //创建了锁 r.lock();   //添加锁 //代码A r.unlock(); // 释放锁

上面意思就是说 这块代码A被加锁了 无法被其他线程抢占

注意:不只是针对同一块代码啊!具体看下面这个栗子:

r.lock();   //添加锁 //代码A r.unlock(); // 释放锁 r.lock();   //添加锁 //代码B r.unlock(); // 释放锁

当线程A执行一个类中的 代码A时 线程B不能执行代码B 虽然他们不是同一个代码块

因为他们来自一个锁r(ReentrantLock)

所谓人一心不可二用 这里也是这个道理。一个锁 只能锁一个代码块

所以 有两种情况不行:

1. 线程A执行代码A 线程B执行代码A2. 线程A执行代码A 线程B执行代码B

第二条拦路虎:如何让B被关进小黑屋(等待)?

.await()函数派上用场了 这就是等待的意思 对应synchronized的wait()函数

线程执行 await() 等待后 当前位置(执行await())下面代码不会被执行

同时释放锁 如果被唤醒了 还是从之前被锁定的位置开始
也可以这么理解 :从哪里跌倒 ,从哪里爬起来

在B线程里执行 c2.await();即可就是把B线程放进c2中等待唤醒

举个栗子:

线程B代码块{ r.lock();   //添加锁 c2.await();      //把B关进c2小黑屋 等待唤醒  r.unlock(); // 释放锁 }
注意了哦! 当线程等待的时候 自动释放锁 很关键 得记住:当调用await()时默认调用unlock()

第三条拦路虎:如何实现ABC线程顺序(这才是关键)

这个时候我们需要一个变量 来指示 当前线程到底时A还是B

1代表A
2代表B
3代表C

public int flag = 1;线程A代码块{if (flag != 1) {        c1.await();    //A关进小黑屋C1         }        c2.signal();   //唤醒小黑屋c2里的家伙(这里指B)}
线程B代码块{if (flag != 2) {        c2.await();    //B关进小黑屋C2         }        c3.signal();   //唤醒小黑屋C3里的家伙(这里指C)}
线程C代码块{if (flag != 3) {        c3.await();    //C关进小黑屋C3         }        c1.signal();   //唤醒小黑屋c1里的家伙(这里指A)}

最后一条拦路虎:如何停止线程ABC

核心代码

举个栗子A:

if(i==1001) {            c2.signal();     //唤醒B            c3.signal();     //唤醒C            r.unlock();      //释放锁         }

r.unlock();这个是关键 一定要写 不然到最后C线程无法停止

因为 虽然唤醒了C1小黑屋里的C
但是: r这个锁还锁着其他线程 所以线程C无法 执行被r锁住的代码 参考第一条拦路虎

还有哦! 我是用 静态变量 i来记录这个递增的数 因为是三个线程共享嘛

提一下哦 !因为ABC线程只执行一次 我是直接采用 匿名对象来实现(实际是有类名的 不过是借用了父类或者接口的类名) 这样子

使用ReentrantLock锁来实现代码:

package day12;import java.sql.Time;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.ReentrantLock;class Printer3 {    private ReentrantLock r = new ReentrantLock();    private Condition c1 = r.newCondition();    private Condition c2 = r.newCondition();    private Condition c3 = r.newCondition();    public static int i = 1;    public int flag = 1;    public void print1() throws InterruptedException {        r.lock();        if (flag != 1) {            c1.await();        }        if(i==1001) {            c2.signal();            c3.signal();            r.unlock();            return;        }        System.out.println("线程A:" + i++);        flag = 2;        c2.signal();        r.unlock(); // 释放锁    }    public void print2() throws InterruptedException {        r.lock();        if (flag != 2) {            c2.await();        }        if(i==1001) {            c1.signal();            c3.signal();            r.unlock();            return;        }        System.out.println("线程B:" + i++);        flag = 3;        c3.signal();        r.unlock();    }    public void print3() throws InterruptedException {        r.lock();        if (flag != 3) {            c3.await();        }        if(i==1001) {               c1.signal();            c2.signal();            r.unlock();            return;        }        System.out.println("线程C:" + i++);        flag = 1;        c1.signal();        r.unlock();    }}public class thread {    public static void main(String[] args) {        final Printer3 p = new Printer3();        long startTime = System.currentTimeMillis();        new Thread() {            public void run() {                while (true) {                    try {                               if (p.i == 1001) {                            System.out.println("线程A结束");                            break;                        }                        p.print1();                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }        }.start();        new Thread() {            public void run() {                while (true) {                    try {                                       if (p.i == 1001) {                            System.out.println("线程B结束");                            break;                        }                        p.print2();                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }        }.start();        new Thread() {            public void run() {                long  startTime   = System.currentTimeMillis(); //程序结束记录时间                while (true) {                    try {                        if (p.i == 1001) {                            System.out.println("线程C结束");                            break;                        }                        p.print3();                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }                long  endTime = System.currentTimeMillis(); //程序结束记录时间                long TotalTime = endTime - startTime;                 System.out.print("耗时:"+TotalTime);            }        }.start();    }}

实验结果:

这里写图片描述

最后提一下 线程 停止有多个方法 :

最好就是让他自己执行完run方法 自然消亡
而不是使用interrupt强制打断

真的是千辛万苦……不过 也算是有点收获了

要是我的这篇博客可以解答你的些许问题,那也是极好的

如果有问题 欢迎提出 一起学习 一起进步。

你可能感兴趣的文章
19 个必须知道的 Visual Studio 快捷键
查看>>
如何在Ubuntu命令行下管理浏览器书签
查看>>
《大数据分析原理与实践》一一2.1 大数据分析模型建立方法
查看>>
《 自动化测试最佳实践:来自全球的经典自动化测试案例解析》一一2.7 测试套件和类型...
查看>>
8月18日云栖精选夜读:阿里视频云最强转码技术揭秘:窄带高清原理解析+用户接入指南...
查看>>
涨姿势:工业物联网与大数据融合的四个重点
查看>>
社会学视角下的大数据方法论及其困境
查看>>
《云计算:原理与范式》一1.7 平台即服务供应商
查看>>
百度成立“百度搜索公司”:固本拓新驱动生态裂变
查看>>
宇宙风暴?才怪!瑞典暗指俄罗斯黑客攻击航空控制系统
查看>>
5G将为欧洲带来超千亿欧元社会经济效益
查看>>
系统进程管理工具Process Explorer
查看>>
富士通仍执着SPARC架构芯片 将坚持推新
查看>>
易宪容:企业要利用大数据挖掘潜在需求
查看>>
微软声称Win10周年更新为Edge浏览器带来更好电池寿命
查看>>
混合云是企业IT的未来吗?
查看>>
LINE在日本取得成功 但全球化之路还很长
查看>>
红帽云套件新增QuickStart Cloud Installer,加快私有云部署
查看>>
MapXtreme 2005 学习心得 一些问题(八)
查看>>
流量精细化运营时代,营销SaaS之使命——流量掘金
查看>>