Java多线程同步锁的理解
java主要通过synchronized的关键字来实现的。让我们从一个买票程序说起吧。
package com.day04; /**
*
* @author Administrator 问题描述:使用多线程的方式来模拟多个窗口买票
*
*/
public class SaleWindow implements Runnable { // 初始化票数10
private int ticket = 10; @Override
public void run() {
// 获取线程的名称,比如Thread-0,并将它截掉Thread-取0这个数字标识,为了构造下面卖票窗口名称
int threadNum = Integer.parseInt(Thread.currentThread().getName().substring(7));
String saleWindowName = "销售窗口" + threadNum;
// 开始买票
while (true) {
if (ticket > 0) {
// 这里为了演示出线程不同步的问题,让线程睡眠一段时间,延时)
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(saleWindowName + " 卖 出 了 " + ticket-- + " 号 票 !");
} else {
break;
}
}
} public static void main(String[] args) {
// 创建了销售窗口对象
SaleWindow sw = new SaleWindow();
// 启动线程,让第一个窗口开始买票
new Thread(sw).start();
// 启动线程,让第二个窗口开始买票
new Thread(sw).start();
// 启动线程,让第三个窗口开始买票
new Thread(sw).start(); } }
运行结果如下所示:
销售窗口2 卖 出 了 10 号 票 !
销售窗口1 卖 出 了 8 号 票 !
销售窗口0 卖 出 了 9 号 票 !
销售窗口2 卖 出 了 7 号 票 !
销售窗口1 卖 出 了 6 号 票 !
销售窗口0 卖 出 了 5 号 票 !
销售窗口2 卖 出 了 4 号 票 !
销售窗口1 卖 出 了 3 号 票 !
销售窗口0 卖 出 了 2 号 票 !
销售窗口2 卖 出 了 1 号 票 !
销售窗口1 卖 出 了 0 号 票 !《-----
销售窗口0 卖 出 了 -1 号 票 !《------
可以看到我们的程序出来了问题,上面打红色箭头所示,竟然卖出了0号票和-1号票了。
让我们画个图来分析一下如下所示:

通过以上分析,不难得出,造成问题原因,是因为同步操作问题。
那我们如何确定哪些是同步操作(或者有同步问题)?
1.明确哪些代码是多线程成运行的代码(run方法中的代码)
2.明确那些是共享数据(ticket票数)
3.明确多线程运行代码中那些语句是操作共享数据(System.out.println(saleWindowName + " 卖 出 了 " + ticket-- + " 号 票 !");)
接下来我们就可以通过Java给我们提供的synchroized关键字使用同步锁来解决以上的问题
package com.day04; /**
* @author Administrator 问题描述:使用多线程的方式来模拟多个窗口买票
*/
public class SaleWindow implements Runnable { // 初始化票数10
private int ticket = 10;
//线程的锁
private Object lock; @Override
public void run() {
// 获取线程的名称,比如Thread-0,并将它截掉Thread-取0这个数字标识,为了构造下面卖票窗口名称
int threadNum = Integer.parseInt(Thread.currentThread().getName().substring(7));
String saleWindowName = "销售窗口" + threadNum;
// 开始买票
while (true) {
//加上synchronized,并加入对象锁,new一个任意对象即可,我们这里使用Object来解决同步问题,注意这里必须是公用同一个锁lock
synchronized (lock) { if (ticket > 0) {
// 这里为了演示出线程不同步的问题,让线程睡眠一段时间,延时)
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(saleWindowName + " 卖 出 了 " + ticket-- + " 号 票 !");
} else {
break;
}
}
}
} public static void main(String[] args) {
// 创建了销售窗口对象
SaleWindow sw = new SaleWindow();
// 启动线程,让第一个窗口开始买票
new Thread(sw).start();
// 启动线程,让第二个窗口开始买票
new Thread(sw).start();
// 启动线程,让第三个窗口开始买票
new Thread(sw).start(); } }
运行结果如下所示:
销售窗口1 卖 出 了 10 号 票 !
销售窗口2 卖 出 了 9 号 票 !
销售窗口0 卖 出 了 8 号 票 !
销售窗口0 卖 出 了 7 号 票 !
销售窗口2 卖 出 了 6 号 票 !
销售窗口1 卖 出 了 5 号 票 !
销售窗口2 卖 出 了 4 号 票 !
销售窗口0 卖 出 了 3 号 票 !
销售窗口0 卖 出 了 2 号 票 !
销售窗口2 卖 出 了 1 号 票 !
这样就有效的解决了同步的问题。
同样我们也可将上面的操作共享数据的同步操作抽取出来,单独封装成一个同步方法,只需要在方法上面的返回值前面加上synchronized关键字即可,这样可以更方面理解和阅读,优化后代码如下。
package com.day04; /**
*
* @author Administrator 问题描述:使用多线程的方式来模拟多个窗口买票
*
*/
public class SaleWindow implements Runnable { // 初始化票数10
private int ticket = 10; @Override
public void run() { // 开始买票
while (true) {
// 当没有票了结束
if (!saleSuccess()) {
break;
}
}
} public synchronized boolean saleSuccess() {
// 获取线程的名称,比如Thread-0,并将它截掉Thread-取0这个数字标识,为了构造下面卖票窗口名称
int threadNum = Integer.parseInt(Thread.currentThread().getName().substring(7));
String saleWindowName = "销售窗口" + threadNum;
if (ticket > 0) {
// 这里为了演示出线程不同步的问题,让线程睡眠一段时间,延时)
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(saleWindowName + " 卖 出 了 " + ticket-- + " 号 票 !");
return true;
} else {
return false;
}
} public static void main(String[] args) {
// 创建了销售窗口对象
SaleWindow sw = new SaleWindow();
// 启动线程,让第一个窗口开始买票
new Thread(sw).start();
// 启动线程,让第二个窗口开始买票
new Thread(sw).start();
// 启动线程,让第三个窗口开始买票
new Thread(sw).start(); } }
销售窗口1 卖 出 了 10 号 票 !销售窗口2 卖 出 了 9 号 票 !销售窗口0 卖 出 了 8 号 票 !销售窗口0 卖 出 了 7 号 票 !销售窗口2 卖 出 了 6 号 票 !销售窗口1 卖 出 了 5 号 票 !销售窗口2 卖 出 了 4 号 票 !销售窗口0 卖 出 了 3 号 票 !销售窗口0 卖 出 了 2 号 票 !销售窗口2 卖 出 了 1 号 票 !
现在又有一个问题出现了,public synchronized boolean saleSuccess()该同步函数用的是哪一个锁?
我们猜想可能用的是this这个对象锁,如果我们让线程一个执行带有sychronized的同步方法,一个执行带有this对象的sychronized同步代码块的方法,如果能够得到正确的结果,不出现同步问题,即论证正确,反之,如果还是出现同步问题即用的不是this这个对象锁。代码如下:
package com.day04; /**
*
* @author Administrator 问题描述:使用多线程的方式来模拟多个窗口买票
*
*/
public class SaleWindow implements Runnable { // 初始化票数10
private int ticket = 10; @Override
public void run() {
// 获取当前线程的序号从0开始
int threadNum = Integer.parseInt(Thread.currentThread().getName().substring(7));
// 偶数线程执行该方法
if ((threadNum + 1) % 2 == 0) {
while (true) {
synchronized (this) {
String saleWindowName = "奇数销售窗口" + threadNum;
if (ticket > 0) {
// 这里为了演示出线程不同步的问题,让线程睡眠一段时间,延时)
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(saleWindowName + " 卖 出 了 " + ticket-- + " 号 票 !");
} else {
break;
}
}
}
} else {
// 奇数线程执行该方法
// 开始买票
while (true) {
// 当没有票了结束
if (!saleSuccess()) {
break;
}
}
} } public synchronized boolean saleSuccess() {
// 获取线程的名称,比如Thread-0,并将它截掉Thread-取0这个数字标识,为了构造下面卖票窗口名称
int threadNum = Integer.parseInt(Thread.currentThread().getName().substring(7));
String saleWindowName = "偶数销售窗口" + threadNum;
if (ticket > 0) {
// 这里为了演示出线程不同步的问题,让线程睡眠一段时间,延时)
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(saleWindowName + " 卖 出 了 " + ticket-- + " 号 票 !");
return true;
} else {
return false;
}
} public static void main(String[] args) {
// 创建了销售窗口对象
SaleWindow sw = new SaleWindow();
// 启动线程,让第一个窗口开始买票
new Thread(sw).start();
// 启动线程,让第二个窗口开始买票
new Thread(sw).start();
// 启动线程,让第三个窗口开始买票
new Thread(sw).start(); } }
运行结果如下:
偶数销售窗口0 卖 出 了 10 号 票 !
偶数销售窗口0 卖 出 了 9 号 票 !
偶数销售窗口2 卖 出 了 8 号 票 !
奇数销售窗口1 卖 出 了 7 号 票 !
偶数销售窗口2 卖 出 了 6 号 票 !
偶数销售窗口2 卖 出 了 5 号 票 !
偶数销售窗口2 卖 出 了 4 号 票 !
偶数销售窗口2 卖 出 了 3 号 票 !
偶数销售窗口2 卖 出 了 2 号 票 !
偶数销售窗口0 卖 出 了 1 号 票 !
由上面的接口即可论证同步方法使用对的对象锁是this。
同样的加入我们的将我们的共享数据ticket改成静态的,并将同步方法也改成静态,它用的是那个对象锁?
我们猜想是本类的class对象这个锁即(SaleWindow.class)这个对象锁。同理如果我们让线程一个执行带有sychronized的静态同步方法,一个执行带有本类的class对象这个锁即(SaleWindow.class)的sychronized同步代码块的方法,如果能够得到正确的结果,不出现同步问题,即论证正确。反之,如果还是出现同步问题,即说明静态同步方法使用的锁不是本类的class对象这个锁即(SaleWindow.class)这个对象锁。代码如下:
package com.day04; /**
*
* @author Administrator 问题描述:使用多线程的方式来模拟多个窗口买票
*
*/
public class SaleWindow implements Runnable { // 初始化票数10
private static int ticket = 10; @Override
public void run() {
// 获取当前线程的序号从0开始
int threadNum = Integer.parseInt(Thread.currentThread().getName().substring(7));
// 偶数线程执行该方法
if ((threadNum + 1) % 2 == 0) {
while (true) {
synchronized (SaleWindow.class) {
String saleWindowName = "奇数销售窗口" + threadNum;
if (ticket > 0) {
// 这里为了演示出线程不同步的问题,让线程睡眠一段时间,延时)
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(saleWindowName + " 卖 出 了 " + ticket-- + " 号 票 !");
} else {
break;
}
}
}
} else {
// 奇数线程执行该方法
// 开始买票
while (true) {
// 当没有票了结束
if (!saleSuccess()) {
break;
}
}
} } public synchronized static boolean saleSuccess() {
// 获取线程的名称,比如Thread-0,并将它截掉Thread-取0这个数字标识,为了构造下面卖票窗口名称
int threadNum = Integer.parseInt(Thread.currentThread().getName().substring(7));
String saleWindowName = "偶数销售窗口" + threadNum;
if (ticket > 0) {
// 这里为了演示出线程不同步的问题,让线程睡眠一段时间,延时)
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(saleWindowName + " 卖 出 了 " + ticket-- + " 号 票 !");
return true;
} else {
return false;
}
} public static void main(String[] args) {
// 创建了销售窗口对象
SaleWindow sw = new SaleWindow();
// 启动线程,让第一个窗口开始买票
new Thread(sw).start();
// 启动线程,让第二个窗口开始买票
new Thread(sw).start();
// 启动线程,让第三个窗口开始买票
new Thread(sw).start(); } }
运行结果如下:
偶数销售窗口0 卖 出 了 10 号 票 !
偶数销售窗口0 卖 出 了 9 号 票 !
偶数销售窗口2 卖 出 了 8 号 票 !
奇数销售窗口1 卖 出 了 7 号 票 !
偶数销售窗口2 卖 出 了 6 号 票 !
偶数销售窗口2 卖 出 了 5 号 票 !
偶数销售窗口2 卖 出 了 4 号 票 !
偶数销售窗口2 卖 出 了 3 号 票 !
偶数销售窗口2 卖 出 了 2 号 票 !
偶数销售窗口0 卖 出 了 1 号 票 !
由上面的接口即可论证同步方法使用对的对象锁是本类的class对象这个锁即(SaleWindow.class)这个对象锁。
public synchronized boolean saleSuccess()
Java多线程同步锁的理解的更多相关文章
- Java 多线程:锁(二)
Java 多线程:锁(二) 作者:Grey 原文地址: 博客园:Java 多线程:锁(二) CSDN:Java 多线程:锁(二) AtomicLong VS LongAddr VS Synchroni ...
- Java多线程同步问题的探究
一.线程的先来后到——问题的提出:为什么要有多线程同步?Java多线程同步的机制是什么? http://www.blogjava.net/zhangwei217245/archive/2010/03/ ...
- 转:关于JAVA多线程同步
转:http://lanvis.blog.163.com/blog/static/26982162009798422547/ 因为需要,最近关注了一下JAVA多线程同步问题.JAVA多线程同步主要依赖 ...
- Java多线程-同步:synchronized 和线程通信:生产者消费者模式
大家伙周末愉快,小乐又来给大家献上技术大餐.上次是说到了Java多线程的创建和状态|乐字节,接下来,我们再来接着说Java多线程-同步:synchronized 和线程通信:生产者消费者模式. 一.同 ...
- 第十五章、Python多线程同步锁,死锁和递归锁
目录 第十五章.Python多线程同步锁,死锁和递归锁 1. 引子: 2.同步锁 3.死锁 引子: 4.递归锁RLock 原理: 不多说,放代码 总结: 5. 大总结 第十五章.Python多线程同步 ...
- JAVA多线程与锁机制
JAVA多线程与锁机制 1 关于Synchronized和lock synchronized是Java的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码 ...
- Java 多线程:锁(一)
Java 多线程:锁(一) 作者:Grey 原文地址: 博客园:Java 多线程:锁(一) CSDN:Java 多线程:锁(一) CAS 比较与交换的意思 举个例子,内存有个值是 3,如果用 Java ...
- Java 多线程:锁(三)
Java 多线程:锁(三) 作者:Grey 原文地址: 博客园:Java 多线程:锁(三) CSDN:Java 多线程:锁(三) StampedLock StampedLock其实是对读写锁的一种改进 ...
- java多线程同步
一篇好文:java多线程机制同步原则 概括起来说,Java 多线程同步机制主要包含如下几点:1:如果一个类包含一个或几个同步方法,那么由此类生成的每一个对象都配备一个队列用来容纳那些等待执行同步的线程 ...
随机推荐
- 【React Native开发】React Native控件之Image组件解说与美团首页顶部效果实例(10)
),React Native技术交流4群(458982758),欢迎各位大牛,React Native技术爱好者增加交流!同一时候博客左側欢迎微信扫描关注订阅号,移动技术干货,精彩文章技术推送! Im ...
- Odoo8在TreeView左上角增加自定义按钮以及通过继承生成自定义的View_Mode
今天有网友在问怎么在TreeView左上角增加一个自定义的按钮,在查询Odoo 自带的模块,发现在purchase_requisition中有使用,并且此模块还应用到了自定义View_Mode的情况, ...
- <四>读<<大话设计模式>>之代理模式
代理模式我想大家即便不熟悉也都听过吧,从字面意思上看就是替别人干活的,比方代理商.在项目的实际应用中也有非常多地方用到.比方spring通过代理模式生成对象等. 代理模式的书面定义:为其它对象提供一种 ...
- Ubuntu——修正LibreOffice中文乱码以及eclipse提示框颜色
刚进ubuntu的时候就老遇到用libreoffice打开某些文档时出现一堆叠在一起的乱码.以及最開始使用eclipse编辑时,鼠标放到一个变量或者函数时 弹出来一黑框框-看的难受的.... 这里记录 ...
- 【Java】Java_12 Eclipse
1.eclipse简介 Eclipse 是一个开放源代码的.基于Java的可扩展开发平台.就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境. 尽管 Eclipse 是使用Java语 ...
- 基于ShaderX5的顶点动画
关于顶点动画,ShaderX5里有篇专门来讲,在虚幻3引擎里也有一个更加丰富的实现.使用了一个Pivot Painter的3dmax脚本.其实自己灵活用vertex color可以避开使用Pivot ...
- find命令下排除部分目录修改权限
#!/bin/bash # 项目文件夹.文件权限修改 # 批量修改文件夹或者文件的权限时,需要先忽略掉可写文件夹('./bootstrap/cache'.'./public/attachments'. ...
- hdu 1829 &poj 2492 A Bug's Life(推断二分图、带权并查集)
A Bug's Life Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) To ...
- DM36x IPNC OSD显示中文 --- 基础知识篇
为了简单起见,只显示GB2312(简体中文)字符一.GB2312汉字编码1.区位码在国标GB2312—80中规定,所有的国标汉字及符号分配在一个94行.94列的方阵中,方阵的每一行称为一个“区”,编号 ...
- Python内置函数之any()
any()函数和all()函数相对立. 相同点为: any()也只能传入一个参数. any()的参数必须是可迭代对象. 不同点: 可迭代对象中只要有一个元素为True,返回值就是True. 下面看看具 ...