本文主要是回顾线程之间互斥和同步,以及线程之间通信,在最开始没有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更好的显示出数据错乱线程

2.现在已经产生了互斥问题
怎么解决上面的问题?
既然产生的原因是在调用这个对象的方法时候造成互斥,那么我就在这个方法或者代码块实现同步
下面用例子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 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
3.分析一下使用Synchronized在不同地方的使用
    (1)同步代码块调用的是当前对象
    (2)在方法上使用同步调用的当前对象
    (3)在静态static方法上使用同步调用的类.class,因为静态方法是类直接调用,锁就是这个类的字节码

 二、线程的通信机制

在讲解线程通信机制之前,我们来看一道面试题:子线程循环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多线程提高二:传统线程的互斥与同步&传统线程通信机制的更多相关文章

  1. “全栈2019”Java多线程第三十七章:如何让等待的线程无法被中断

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  2. “全栈2019”Java多线程第二十六章:同步方法生产者与消费者线程

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  3. JAVA多线程提高一:传统线程技术&传统定时器Timer

    前面我们已经对多线程的基础知识有了一定的了解,那么接下来我们将要对多线程进一步深入的学习:但在学习之前我们还是要对传统的技术进行一次回顾,本章我们回顾的则是:传统线程技术和传统的定时器实现. 一.传统 ...

  4. java多线程详解(3)-线程的互斥与同步

    前言:前一篇文章主要描述了多线程中访成员变量与局部变量问题,我们知道访成员变量有线程安全问题,在多线程程序中 我们可以通过使用synchronized关键字完成线程的同步,能够解决部分线程安全问题 在 ...

  5. JAVA多线程提高三:线程范围内共享变量&ThreadLocal

    今天我们学习的是如何在线程自己的范围内达到变量数据的共享,而各个线程之间又是互相独立开来,各自维护的,即我们说的ThreadLocal的作用. 一.概念 可以将每个线程用到的数据与对应的线程号存放到一 ...

  6. Java多线程系列--“JUC锁”02之 互斥锁ReentrantLock

    本章对ReentrantLock包进行基本介绍,这一章主要对ReentrantLock进行概括性的介绍,内容包括:ReentrantLock介绍ReentrantLock函数列表ReentrantLo ...

  7. Java多线程开发系列之番外篇:事件派发线程---EventDispatchThread

    事件派发线程是java Swing开发中重要的知识点,在安卓app开发中,也是非常重要的一点.今天我们在多线程开发中,穿插进来这个线程.分别从线程的来由.原理和使用方法三个方面来学习事件派发线程. 一 ...

  8. Java 多线程详解(二)------如何创建进程和线程

    Java 多线程详解(一)------概念的引入:http://www.cnblogs.com/ysocean/p/6882988.html 在上一篇博客中,我们已经介绍了并发和并行的区别,以及进程和 ...

  9. java 多线程系列基础篇(九)之interrupt()和线程终止方式

    1. interrupt()说明 在介绍终止线程的方式之前,有必要先对interrupt()进行了解.关于interrupt(),java的djk文档描述如下:http://docs.oracle.c ...

随机推荐

  1. bat获取当前日期的前一天

    批处理做这样的事情很麻烦,你可以用cscript来实现,比如把下面的内容保存为a.js文件:var d=new Date();d.setTime(d.getTime()-24*3600*1000);v ...

  2. C#高级编程 (第六版) 学习 第七章:委托和事件

    第七章 委托和事件 回调(callback)函数是Windows编程的一个重要方面,实际上是方法调用的指针,也称为函数指针. .Net以委托的形式实现了函数指针的概念,.Net的委托是类型安全的. 委 ...

  3. WPF和Expression Blend开发实例:Loading动画

    今天来点实际的,项目中可以真实使用的,一个Loading的动画,最后封装成一个控件,可以直接使用在项目中,先上图: 整个设计比较简单,就是在界面上画18个Path,然后通过动画改变OpacityMas ...

  4. 【第九周】beta-review阶段贡献分分配

    组名: 新蜂 组长: 武志远 组员: 宫成荣 谢孝淼 杨柳 李峤 项目名称: java俄罗斯方块NEO 武志远 武志远 武志远 武志远 武志远 宫成荣 宫成荣 杨柳 宫成荣 宫成荣 李峤 杨柳 李峤 ...

  5. mysql中联合查询

    联合查询union 一个翻译问题的解释: 在mysql的手册中,将连接查询(Join)翻译为联合查询: 而联合查询(union),没有明确翻译. 但: 在通常的书籍或文章中,join被翻译为“连接”查 ...

  6. 【loj6038】「雅礼集训 2017 Day5」远行 树的直径+并查集+LCT

    题目描述 给你 $n$ 个点,支持 $m$ 次操作,每次为以下两种:连一条边,保证连完后是一棵树/森林:询问一个点能到达的最远的点与该点的距离.强制在线. $n\le 3\times 10^5$ ,$ ...

  7. [poi2011]bzoj 2277 —— strongbox·[洛谷3518]

    ·问题描述· 有一个密码箱,0到n-1中的某些数是它的密码.且满足:如果a和b都是它的密码,那么(a+b)%n也是它的密码.某人试了k次密码,前k-1次都失败了,最后一次成功. 问:该密码箱最多有多少 ...

  8. DjangoORM使用mysql注意

    注意事项1:需要在project下的setting里面做设置.让Django生成MySQL类型的数据库. 注意事项2:在Django内部,连MySQL的时候,需要添加下面2句代码: 4.******* ...

  9. DjangoORM创建表结构以及生成数据库结构

    1. ORM的两种 DB first: 创建表结构--根据表结构生成类-----根据类来操作数据库 Code first: 先写代码------再写类----执行命令(一个类生成一个表)当前主流的用法 ...

  10. React Native工程中TSLint静态检查工具的探索之路

    建立的代码规范没人遵守,项目中遍地风格迥异的代码,你会不会抓狂? 通过测试用例的程序还会出现Bug,而原因仅仅是自己犯下的低级错误,你会不会抓狂? 某种代码写法存在问题导致崩溃时,只能全工程检查代码, ...