JAVA多线程提高二:传统线程的互斥与同步&传统线程通信机制
本文主要是回顾线程之间互斥和同步,以及线程之间通信,在最开始没有juc并发包情况下,如何实现的,也就是我们传统的方式如何来实现的,回顾知识是为了后面的提高作准备。
一、线程的互斥
为什么会有线程的互斥?可以想银行取款的问题,如果不做监控,多个人同时针对一个存折取钱的时候就会出现钱不对的问题,
下面我们通过两个例子来分析一下线程的互斥问题以及为什么会产生这个线程?
例子1:一个人生产信息,一个人消费信息
面向对象的思想:类 信息类 生产者 消费者
public class TriditionalThreadSafeLxh {
public static void main(String[] args) {
// 多个线程调用同一个对象,会带来数据错乱线程,产生的原因分析:
// 多个线程调用一个对象的某个方法
Info info = new Info();
Production p = new Production(info);
Consumer c = new Consumer(info);
new Thread(p).start();
new Thread(c).start();
}
}
class Info {
private String name = "李兴华";
private String content = "讲师";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
class Production implements Runnable {
private Info info; public Production(Info info) {
this.info = info;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if (i % 2 == 0) {
this.info.setName("李兴华");
try {
Thread.sleep(80);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.info.setContent("讲师");
}else{
this.info.setName("mldn");
try {
Thread.sleep(80);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.info.setContent("www.mldn.cn");
}
}
}
}
class Consumer implements Runnable{
private Info info; public Consumer(Info info) {
this.info = info;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(80);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.info.getName()+","+this.info.getContent());
}
} }
输出:
mldn,讲师
李兴华,www.mldn.cn
mldn,讲师
李兴华,www.mldn.cn
mldn,讲师
李兴华,www.mldn.cn
mldn,讲师
李兴华,www.mldn.cn
mldn,讲师
李兴华,www.mldn.cn
mldn,讲师
李兴华,www.mldn.cn
mldn,讲师
李兴华,www.mldn.cn
mldn,讲师
李兴华,www.mldn.cn
mldn,讲师
李兴华,www.mldn.cn
mldn,讲师
mldn,www.mldn.cn
例子2:多个线程同时打印名字
public class TraditionalThreadSafe { public static void main(String[] args) {
new TraditionalThreadSafe().init();
} public void init(){
final OutputMessage c = new OutputMessage();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(120);
} catch (InterruptedException e) {
e.printStackTrace();
}
c.output("aaaaa");
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(120);
} catch (InterruptedException e) {
e.printStackTrace();
}
c.output("bbbbb");
}
}).start(); } class OutputMessage{
public void output(String name){
while(true){
for (int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));//打印一个人的名字
}
System.out.println();
}
}
}
}
输出结果:
bbbbb
bbbbb
aaaa
aabbbbb
bbbbb
bbbbb
通过上面两个例子我们分析得出线程互斥问题产生的原因:
(1)多个线程调用同一个对象的某个方法
(2)在线程的run()方法中使用sleep更好的显示出数据错乱线程
public class TraditionalThreadSafe { public static void main(String[] args) {
new TraditionalThreadSafe().init();
} public void init(){
final OutputMessage c = new OutputMessage();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(120);
} catch (InterruptedException e) {
e.printStackTrace();
}
c.output("aaaaa");
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(120);
} catch (InterruptedException e) {
e.printStackTrace();
}
c.output("bbbbb");
}
}).start(); } class OutputMessage{
public synchronized void output(String name){
// synchronized (this) {
while(true){
for (int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));//打印一个人的名字
}
System.out.println();
}
// } }
}
}
运行结果:
bbbbb
bbbbb
bbbbb
bbbbb
bbbbb
二、线程的通信机制
在讲解线程通信机制之前,我们来看一道面试题:子线程循环10次,接着主线程循环100,接着又回到子线程循环10次, 接着再回到主线程又循环100,如此循环50次
分析:
1、主进程子进程存在线程同步问题,对于同步的内容应该封装在一个类中,在类中定义主进程和子进程需要操作的方法,通过获得锁而执行各自的方法。
2、这里存在子进程和主进程交替运行,应该添加一个信号变量,主进程和子进程判断该状态是否可以执行,主进程或子进程一次循环结束重置该变量值,然后调用notify(notifyallAll)方法来唤醒其他等待共享对象释放的线程。
实现:
定义一个类Business,定义主进程和子进程执行的方法,主进程和子进程对同一个对象business操作,通过使用synchronized作用在该对象相应的方法 从而达到同步的作用。类Business中定义一个交换变量bShouldSub来使得进程交替执行,主进程或子进程执行完都会更改该变量的值,然后调用this.notify
来唤醒其他等待对象business(this)的线程。
public class TraditionalThreadCommucation {
public static void main(String[] args) {
final Business b = new Business(); new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <=50; i++) {
b.sub(i);
}
}
}).start(); for (int i = 1; i <=50; i++) {
b.main(i);
}
}
}
class Business{
private boolean isShouldbeSub=true; //标记为 判断是否该子线程运行
public synchronized void sub(int i){
while(!isShouldbeSub){ //不该子线程运行的时候进来了
try {
this.wait(); //等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int j = 1; j <= 10; j++) {
System.out.println("子线程循环 : "+j+",外层循环: "+i);
}
isShouldbeSub=false;
this.notifyAll();
} public synchronized void main(int i){
while(isShouldbeSub){ //不该主线程运行的时候进来了,检查
try {
this.wait(); //等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int j = 1; j <= 100; j++) {
System.out.println("主线程循环 : "+j+",外层循环: "+i);
}
isShouldbeSub=true;
this.notifyAll();
}
}
运行结果:
子线程循环 : 1,外层循环: 1
子线程循环 : 2,外层循环: 1
子线程循环 : 3,外层循环: 1
子线程循环 : 4,外层循环: 1
子线程循环 : 5,外层循环: 1
子线程循环 : 6,外层循环: 1
子线程循环 : 7,外层循环: 1
子线程循环 : 8,外层循环: 1
子线程循环 : 9,外层循环: 1
子线程循环 : 10,外层循环: 1
主线程循环 : 1,外层循环: 1
主线程循环 : 2,外层循环: 1
主线程循环 : 3,外层循环: 1
主线程循环 : 4,外层循环: 1
主线程循环 : 5,外层循环: 1
主线程循环 : 6,外层循环: 1
主线程循环 : 7,外层循环: 1
主线程循环 : 8,外层循环: 1
主线程循环 : 9,外层循环: 1
主线程循环 : 10,外层循环: 1
主线程循环 : 11,外层循环: 1
主线程循环 : 12,外层循环: 1
主线程循环 : 13,外层循环: 1
主线程循环 : 14,外层循环: 1
主线程循环 : 15,外层循环: 1
主线程循环 : 16,外层循环: 1
主线程循环 : 17,外层循环: 1
主线程循环 : 18,外层循环: 1
主线程循环 : 19,外层循环: 1
主线程循环 : 20,外层循环: 1
主线程循环 : 21,外层循环: 1
主线程循环 : 22,外层循环: 1
主线程循环 : 23,外层循环: 1
主线程循环 : 24,外层循环: 1
主线程循环 : 25,外层循环: 1
主线程循环 : 26,外层循环: 1
主线程循环 : 27,外层循环: 1
主线程循环 : 28,外层循环: 1
主线程循环 : 29,外层循环: 1
主线程循环 : 30,外层循环: 1
主线程循环 : 31,外层循环: 1
主线程循环 : 32,外层循环: 1
主线程循环 : 33,外层循环: 1
主线程循环 : 34,外层循环: 1
主线程循环 : 35,外层循环: 1
主线程循环 : 36,外层循环: 1
主线程循环 : 37,外层循环: 1
主线程循环 : 38,外层循环: 1
主线程循环 : 39,外层循环: 1
主线程循环 : 40,外层循环: 1
主线程循环 : 41,外层循环: 1
主线程循环 : 42,外层循环: 1
主线程循环 : 43,外层循环: 1
主线程循环 : 44,外层循环: 1
主线程循环 : 45,外层循环: 1
主线程循环 : 46,外层循环: 1
主线程循环 : 47,外层循环: 1
主线程循环 : 48,外层循环: 1
主线程循环 : 49,外层循环: 1
主线程循环 : 50,外层循环: 1
主线程循环 : 51,外层循环: 1
主线程循环 : 52,外层循环: 1
主线程循环 : 53,外层循环: 1
主线程循环 : 54,外层循环: 1
主线程循环 : 55,外层循环: 1
主线程循环 : 56,外层循环: 1
主线程循环 : 57,外层循环: 1
主线程循环 : 58,外层循环: 1
主线程循环 : 59,外层循环: 1
主线程循环 : 60,外层循环: 1
主线程循环 : 61,外层循环: 1
主线程循环 : 62,外层循环: 1
主线程循环 : 63,外层循环: 1
主线程循环 : 64,外层循环: 1
主线程循环 : 65,外层循环: 1
主线程循环 : 66,外层循环: 1
主线程循环 : 67,外层循环: 1
主线程循环 : 68,外层循环: 1
主线程循环 : 69,外层循环: 1
主线程循环 : 70,外层循环: 1
主线程循环 : 71,外层循环: 1
主线程循环 : 72,外层循环: 1
主线程循环 : 73,外层循环: 1
主线程循环 : 74,外层循环: 1
主线程循环 : 75,外层循环: 1
主线程循环 : 76,外层循环: 1
主线程循环 : 77,外层循环: 1
主线程循环 : 78,外层循环: 1
主线程循环 : 79,外层循环: 1
主线程循环 : 80,外层循环: 1
主线程循环 : 81,外层循环: 1
主线程循环 : 82,外层循环: 1
主线程循环 : 83,外层循环: 1
主线程循环 : 84,外层循环: 1
主线程循环 : 85,外层循环: 1
主线程循环 : 86,外层循环: 1
主线程循环 : 87,外层循环: 1
主线程循环 : 88,外层循环: 1
主线程循环 : 89,外层循环: 1
主线程循环 : 90,外层循环: 1
主线程循环 : 91,外层循环: 1
主线程循环 : 92,外层循环: 1
主线程循环 : 93,外层循环: 1
主线程循环 : 94,外层循环: 1
主线程循环 : 95,外层循环: 1
主线程循环 : 96,外层循环: 1
主线程循环 : 97,外层循环: 1
主线程循环 : 98,外层循环: 1
主线程循环 : 99,外层循环: 1
主线程循环 : 100,外层循环: 1
子线程循环 : 1,外层循环: 2
子线程循环 : 2,外层循环: 2
子线程循环 : 3,外层循环: 2
子线程循环 : 4,外层循环: 2
子线程循环 : 5,外层循环: 2
子线程循环 : 6,外层循环: 2
子线程循环 : 7,外层循环: 2
子线程循环 : 8,外层循环: 2
子线程循环 : 9,外层循环: 2
子线程循环 : 10,外层循环: 2
主线程循环 : 1,外层循环: 2
主线程循环 : 2,外层循环: 2
主线程循环 : 3,外层循环: 2
主线程循环 : 4,外层循环: 2
主线程循环 : 5,外层循环: 2
主线程循环 : 6,外层循环: 2
主线程循环 : 7,外层循环: 2
主线程循环 : 8,外层循环: 2
主线程循环 : 9,外层循环: 2
主线程循环 : 10,外层循环: 2
主线程循环 : 11,外层循环: 2
主线程循环 : 12,外层循环: 2
主线程循环 : 13,外层循环: 2
主线程循环 : 14,外层循环: 2
主线程循环 : 15,外层循环: 2
主线程循环 : 16,外层循环: 2
主线程循环 : 17,外层循环: 2
主线程循环 : 18,外层循环: 2
主线程循环 : 19,外层循环: 2
主线程循环 : 20,外层循环: 2
主线程循环 : 21,外层循环: 2
主线程循环 : 22,外层循环: 2
主线程循环 : 23,外层循环: 2
主线程循环 : 24,外层循环: 2
主线程循环 : 25,外层循环: 2
主线程循环 : 26,外层循环: 2
主线程循环 : 27,外层循环: 2
主线程循环 : 28,外层循环: 2
主线程循环 : 29,外层循环: 2
主线程循环 : 30,外层循环: 2
主线程循环 : 31,外层循环: 2
主线程循环 : 32,外层循环: 2
主线程循环 : 33,外层循环: 2
主线程循环 : 34,外层循环: 2
主线程循环 : 35,外层循环: 2
主线程循环 : 36,外层循环: 2
主线程循环 : 37,外层循环: 2
主线程循环 : 38,外层循环: 2
主线程循环 : 39,外层循环: 2
主线程循环 : 40,外层循环: 2
主线程循环 : 41,外层循环: 2
主线程循环 : 42,外层循环: 2
主线程循环 : 43,外层循环: 2
主线程循环 : 44,外层循环: 2
主线程循环 : 45,外层循环: 2
主线程循环 : 46,外层循环: 2
主线程循环 : 47,外层循环: 2
主线程循环 : 48,外层循环: 2
主线程循环 : 49,外层循环: 2
主线程循环 : 50,外层循环: 2
主线程循环 : 51,外层循环: 2
主线程循环 : 52,外层循环: 2
主线程循环 : 53,外层循环: 2
主线程循环 : 54,外层循环: 2
主线程循环 : 55,外层循环: 2
主线程循环 : 56,外层循环: 2
主线程循环 : 57,外层循环: 2
主线程循环 : 58,外层循环: 2
主线程循环 : 59,外层循环: 2
主线程循环 : 60,外层循环: 2
主线程循环 : 61,外层循环: 2
主线程循环 : 62,外层循环: 2
主线程循环 : 63,外层循环: 2
主线程循环 : 64,外层循环: 2
主线程循环 : 65,外层循环: 2
主线程循环 : 66,外层循环: 2
主线程循环 : 67,外层循环: 2
主线程循环 : 68,外层循环: 2
主线程循环 : 69,外层循环: 2
主线程循环 : 70,外层循环: 2
主线程循环 : 71,外层循环: 2
主线程循环 : 72,外层循环: 2
主线程循环 : 73,外层循环: 2
主线程循环 : 74,外层循环: 2
主线程循环 : 75,外层循环: 2
主线程循环 : 76,外层循环: 2
主线程循环 : 77,外层循环: 2
主线程循环 : 78,外层循环: 2
主线程循环 : 79,外层循环: 2
主线程循环 : 80,外层循环: 2
主线程循环 : 81,外层循环: 2
主线程循环 : 82,外层循环: 2
主线程循环 : 83,外层循环: 2
主线程循环 : 84,外层循环: 2
主线程循环 : 85,外层循环: 2
主线程循环 : 86,外层循环: 2
主线程循环 : 87,外层循环: 2
主线程循环 : 88,外层循环: 2
主线程循环 : 89,外层循环: 2
主线程循环 : 90,外层循环: 2
主线程循环 : 91,外层循环: 2
主线程循环 : 92,外层循环: 2
主线程循环 : 93,外层循环: 2
主线程循环 : 94,外层循环: 2
主线程循环 : 95,外层循环: 2
主线程循环 : 96,外层循环: 2
主线程循环 : 97,外层循环: 2
主线程循环 : 98,外层循环: 2
主线程循环 : 99,外层循环: 2
主线程循环 : 100,外层循环: 2
子线程循环 : 1,外层循环: 3
子线程循环 : 2,外层循环: 3
子线程循环 : 3,外层循环: 3
子线程循环 : 4,外层循环: 3
子线程循环 : 5,外层循环: 3
子线程循环 : 6,外层循环: 3
子线程循环 : 7,外层循环: 3
子线程循环 : 8,外层循环: 3
子线程循环 : 9,外层循环: 3
注意:
<<Effective Java>>中提到,永远不要在循环之外调用wait方法。因为,参考:为什么wait()语句要放在while循环之内
参考资料:
《多线程视频》张孝祥
JAVA多线程提高二:传统线程的互斥与同步&传统线程通信机制的更多相关文章
- “全栈2019”Java多线程第三十七章:如何让等待的线程无法被中断
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- “全栈2019”Java多线程第二十六章:同步方法生产者与消费者线程
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- JAVA多线程提高一:传统线程技术&传统定时器Timer
前面我们已经对多线程的基础知识有了一定的了解,那么接下来我们将要对多线程进一步深入的学习:但在学习之前我们还是要对传统的技术进行一次回顾,本章我们回顾的则是:传统线程技术和传统的定时器实现. 一.传统 ...
- java多线程详解(3)-线程的互斥与同步
前言:前一篇文章主要描述了多线程中访成员变量与局部变量问题,我们知道访成员变量有线程安全问题,在多线程程序中 我们可以通过使用synchronized关键字完成线程的同步,能够解决部分线程安全问题 在 ...
- JAVA多线程提高三:线程范围内共享变量&ThreadLocal
今天我们学习的是如何在线程自己的范围内达到变量数据的共享,而各个线程之间又是互相独立开来,各自维护的,即我们说的ThreadLocal的作用. 一.概念 可以将每个线程用到的数据与对应的线程号存放到一 ...
- Java多线程系列--“JUC锁”02之 互斥锁ReentrantLock
本章对ReentrantLock包进行基本介绍,这一章主要对ReentrantLock进行概括性的介绍,内容包括:ReentrantLock介绍ReentrantLock函数列表ReentrantLo ...
- Java多线程开发系列之番外篇:事件派发线程---EventDispatchThread
事件派发线程是java Swing开发中重要的知识点,在安卓app开发中,也是非常重要的一点.今天我们在多线程开发中,穿插进来这个线程.分别从线程的来由.原理和使用方法三个方面来学习事件派发线程. 一 ...
- Java 多线程详解(二)------如何创建进程和线程
Java 多线程详解(一)------概念的引入:http://www.cnblogs.com/ysocean/p/6882988.html 在上一篇博客中,我们已经介绍了并发和并行的区别,以及进程和 ...
- java 多线程系列基础篇(九)之interrupt()和线程终止方式
1. interrupt()说明 在介绍终止线程的方式之前,有必要先对interrupt()进行了解.关于interrupt(),java的djk文档描述如下:http://docs.oracle.c ...
随机推荐
- lintcode-445-余弦相似度
445-余弦相似度 Cosine similarity is a measure of similarity between two vectors of an inner product space ...
- Scrum 项目 5.0
5.0--------------------------------------------------- 1.团队成员完成自己认领的任务. 2.燃尽图:理解.设计并画出本次Sprint的燃尽图的理 ...
- 1st 英文文章词频统计
英文文章词频统计: 功能:统计一篇英文文章的单词总数及出现频数并输出,之后排序,输出频数前十的单词及其频数. 实现方法:使用C语言,用fopen函数读入txt文件,fscanf函数逐个读入单词,结构体 ...
- jQuery的滚动监听
jQuery的滚动监听 1.当前滚动的地方的窗口顶端到整个页面顶端的距离: var winPos = $(window).scrollTop(); 2.获取指定元素的页面位置: $(val).offs ...
- 还原 listagg/wm_concat 后的数据 pack_split_listatt ;
1.创建表并制作测试数据: --创建测试表 : CREATE TABLE split_table ( NAME ), ID ) ); --准备测试数据 : INSERT INTO split_tabl ...
- Git命令常用清单
本文从以下十个方面,介绍Git命令的常用清单: 一.新建代码库 二.配置 三.增加/删除文件 四.代码提交 五.分支 六.标签 七.查看信息 八.远程同步 九.撤销 十.其他 每天使用 Git ,但是 ...
- 第96天:CSS3 背景详解
一.背景大小 background: url("images/bg.jpg") no-repeat;控制背景的大小1.具体数值background-size: 500px 500p ...
- 【uoj#21】[UR #1]缩进优化 数学
题目描述 给出 $n$ 个数 ,求 $\text{Min}_{x=1}^{\infty}\sum\limits_{i=1}^n(\lfloor\frac {a_i}x\rfloor+a_i\ \tex ...
- window与linux查看端口被占用
本文摘写自: 百度经验 https://www.cnblogs.com/ieayoio/p/5757198.html 一.windows:开始---->运行---->cmd,或者是wind ...
- Docker学习笔记一:如何在线安装
一.Docker简介: Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从Apache2.0协议开源.Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级.可移植的容器中,然后 ...