本文主要是回顾线程之间互斥和同步,以及线程之间通信,在最开始没有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. C#中委托的理解

    请注意,这只是个人关于C#中委托的一点点理解,参考了一些博客,如有不周之处,请指出,谢谢! 委托是一种函数指针,委托是方法的抽象,方法是委托的实例.委托是C#语言的一道坎,明白了委托才能算是C#真正入 ...

  2. lintcode-488-快乐数

    488-快乐数 写一个算法来判断一个数是不是"快乐数". 一个数是不是快乐是这么定义的:对于一个正整数,每一次将该数替换为他每个位置上的数字的平方和,然后重复这个过程直到这个数变为 ...

  3. CentOS 7 U盘安装问题解决

    最近期待以久的CentOS 7正式版终于发布了,在家里无聊,所以就打算在我的小Y上安装一下,由于笔记本原来有安装Windows 7 操作系统,考虑使用的需求,所以决定安装双系统: 1.         ...

  4. web.py 中文模版报错

    1. 作为模板的html文件,必须是utf-8编码; 2. html文件内容中的charset必须为utf-8,也就是必须包含 <meta http-equiv="Content-Ty ...

  5. display:table的几个妙用:垂直居中、浮动……

    一.为什么不用table系表格元素? 目前,在大多数开发环境中,已经基本不用table元素来做网页布局了,取而代之的是div+css,那么为什么不用table系表格元素呢? 1.用DIV+CSS编写出 ...

  6. 第201天:js---实现继承的5种方式

    一.构造函数方式 //构造函数 function People(){ this.race = '汉族'; } People.prototype={ eat:function(){ console.lo ...

  7. mvc4中使用angularjs实现一个投票系统

    数据库是用EF操作,数据表都很简单中,从代码中也能猜出表的结构,所以关于数据库表就不列出了 投票系统实现还是比较简单,投票部分使用ajax实现,评论部分是使用angularjs实现,并且页面每隔几秒( ...

  8. java 父类的引用调用自己的属性 但是调用的方法必须是重写过的父类的方法 因为编译时候把他当作父类 运行时候才是他自己 所以必须重写父类得方法

  9. 基于jwt的token验证

    一.什么是JWT Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519). 该token被设计为紧凑且安全的,特别适用于分布 ...

  10. Linux学习笔记一:Linux配置java环境变量

    一.安装JDK: 1.创建JDK的安装目录: sudo mkdir /usr/jdk 2.将jdk-7u25-linux-x64.tar.gz拷贝至/usr/jdk目录下 sudo cp jdk-7u ...