(Java多线程系列二)线程间同步
Java多线程间同步
1、什么是线程安全
通过一个案例了解线程安全
案例:需求现在有100张火车票,有两个窗口同时抢火车票,请使用多线程模拟抢票效果。
先来看一个线程不安全的例子
class SellTicketRunnable implements Runnable {
public int count = 100;
@Override
public void run() {
while (count > 0) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
int index = 100 - count + 1;
System.out.println(Thread.currentThread().getName() + "卖出第" + index + "张票");
count--;
}
}
}
public class JavaSyncDemo {
public static void main(String[] args) {
SellTicketRunnable runnable = new SellTicketRunnable();
Thread sellThread1 = new Thread(runnable);
Thread sellThread2 = new Thread(runnable);
sellThread1.start();
sellThread2.start();
}
}
可以看到两个线程同时卖票的时候,会出现漏卖,多卖同一张票,还会出现超卖的问题,这就是线程不安全的问题。
当多个线程同时共享,同一个全局变量或静态变量,做写的操作时,可能会发生数据冲突问题,也就是线程安全问题。但是做读操作是不会发生数据冲突问题。
2、线程安全问题的解决办法
(1)使用同步代码块
class SellTicketRunnable implements Runnable {
public int count = 100;
private Object lock = new Object();
@Override
public void run() {
while (count > 0) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock) {
if (count > 0) {
int index = 100 - count + 1;
System.out.println(Thread.currentThread().getName() + "卖出第" + index + "张票");
count--;
}
}
}
}
}
public class JavaSyncDemo {
public static void main(String[] args) {
SellTicketRunnable runnable = new SellTicketRunnable();
Thread sellThread1 = new Thread(runnable);
Thread sellThread2 = new Thread(runnable);
sellThread1.start();
sellThread2.start();
}
}
从上面的案例可以看出,使用synchronized同步代码块包裹住写操作,每个线程在调用同步代码块中逻辑的时候,都需要先获取同步锁,所以避免了多线程写操作数据的冲突问题。
(2)使用同步函数
class SellTicketRunnable01 implements Runnable {
public int count = 100;
@Override
public void run() {
while (count > 0) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.sale();
}
}
synchronized void sale() {
if (count > 0) {
int index = 100 - count + 1;
System.out.println(Thread.currentThread().getName() + "卖出第" + index + "张票");
count--;
}
}
}
public class JavaSyncDemo01 {
public static void main(String[] args) {
SellTicketRunnable01 runnable = new SellTicketRunnable01();
Thread sellThread1 = new Thread(runnable);
Thread sellThread2 = new Thread(runnable);
sellThread1.start();
sellThread2.start();
}
}
synchronized包裹的函数,其实就是给该函数块添加了一把this锁。
注意:synchronized 修饰静态方法使用锁是当前类的字节码文件(即
类名.class
),同理,如果在静态方法中添加个同步代码块,可以获取类名.class
为代码块加锁
class SellTicketRunnable02 implements Runnable {
public static int count = 100;
@Override
public void run() {
while (count > 0) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
SellTicketRunnable02.sale();
}
}
static void sale() {
synchronized (SellTicketRunnable02.class) {
if (count > 0) {
int index = 100 - count + 1;
System.out.println(Thread.currentThread().getName() + "卖出第" + index + "张票");
count--;
}
}
}
}
public class JavaSyncDemo02 {
public static void main(String[] args) {
SellTicketRunnable02 runnable = new SellTicketRunnable02();
Thread sellThread1 = new Thread(runnable);
Thread sellThread2 = new Thread(runnable);
sellThread1.start();
sellThread2.start();
}
}
(3)使用lock锁
class SellTicketRunnable03 implements Runnable {
public int count = 100;
private Lock lock = new ReentrantLock();
@Override
public void run() {
while (count > 0) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();
if (count > 0) {
int index = 100 - count + 1;
System.out.println(Thread.currentThread().getName() + "卖出第" + index + "张票");
count--;
}
lock.unlock();
}
}
}
public class JavaSyncDemo03 {
public static void main(String[] args) {
SellTicketRunnable03 runnable = new SellTicketRunnable03();
Thread sellThread1 = new Thread(runnable);
Thread sellThread2 = new Thread(runnable);
sellThread1.start();
sellThread2.start();
}
}
lock和synchronized的区别
①lock在使用时需要手动的获取锁和释放锁;
②lock可以尝试非阻塞的获取锁,如果这一时刻锁没有被其他线程获取到,则成功获取并持有锁;
③lock锁可以响应中断,当获取到锁的线程被中断时,中断异常会被抛出,同时锁被释放;
④lock在指定截至时间之前获取锁,如果解释时间到了依旧无法获取锁,就返回。
// lock锁的安全使用方法
class lockDemo {
Lock lock = new ReentrantLock();
void demoFun() {
lock.lock();
try {
// 可能出现线程安全的操作
} finally {
lock.unlock();
}
}
}
(4)使用Java原子类
java.util.concurrent.atomic.AtomicBoolean;
java.util.concurrent.atomic.AtomicInteger;
java.util.concurrent.atomic.AtomicLong;
java.util.concurrent.atomic.AtomicReference;
class SellTicketRunnable04 implements Runnable {
public AtomicInteger count = new AtomicInteger(100);
@Override
public void run() {
while (true) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count.get() > 0) {
int index = 100 - count.getAndDecrement() + 1;
System.out.println(Thread.currentThread().getName() + "卖出第" + index + "张票");
}
}
}
}
public class JavaSyncDemo04 {
public static void main(String[] args) {
SellTicketRunnable04 runnable = new SellTicketRunnable04();
Thread sellThread1 = new Thread(runnable);
Thread sellThread2 = new Thread(runnable);
sellThread1.start();
sellThread2.start();
}
}
3、死锁
先看一个死锁的示例
public class DeadLockDemo01 {
private static Object lock1 = new Object();
private static Object lock2 = new Object();
public static void main(String[] args) {
new Thread() { //线程1
public void run() {
while (true) {
synchronized (lock1) {
System.out.println(this.getName() + ":获取lock1锁");
synchronized (lock2) {
System.out.println(this.getName() + ":获取lock2锁");
}
}
}
}
}.start();
new Thread() { //线程2
public void run() {
while (true) {
synchronized (lock2) {
System.out.println(this.getName() + ":获取lock2锁");
synchronized (lock1) {
System.out.println(this.getName() + "::获取lock1锁");
}
}
}
}
}.start();
}
}
运行上面的代码,可以观察到线程卡死,就是出现了死锁
线程1先拿到lock1锁,再拿到lock2锁,执行完成后才能释放所有锁;
线程2先拿到lock2锁,再拿到lock1锁,执行完成后才能释放所有锁。
如果在线程1获取到lock1锁的时候,线程2获取到lock2还没释放,线程1无法获取lock2锁,也就无法释放lock2锁,这时系统就会出现死锁。
线程死锁的避免办法:不要在同步中嵌套同步
(Java多线程系列二)线程间同步的更多相关文章
- Java多线程系列--“JUC线程池”03之 线程池原理(二)
概要 在前面一章"Java多线程系列--“JUC线程池”02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包括:线程池示例参考代 ...
- Java多线程系列--“JUC线程池”06之 Callable和Future
概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...
- Java多线程系列--“JUC线程池”04之 线程池原理(三)
转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509960.html 本章介绍线程池的生命周期.在"Java多线程系列--“基础篇”01之 基 ...
- Java多线程系列--“JUC线程池”05之 线程池原理(四)
概要 本章介绍线程池的拒绝策略.内容包括:拒绝策略介绍拒绝策略对比和示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3512947.html 拒绝策略 ...
- Java多线程系列--“JUC线程池”02之 线程池原理(一)
概要 在上一章"Java多线程系列--“JUC线程池”01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我们通过分析Th ...
- java多线程系列(二)
对象变量的并发访问 前言:本系列将从零开始讲解java多线程相关的技术,内容参考于<java多线程核心技术>与<java并发编程实战>等相关资料,希望站在巨人的肩膀上,再通过我 ...
- java多线程系列(二)---对象变量并发访问
对象变量的并发访问 前言:本系列将从零开始讲解java多线程相关的技术,内容参考于<java多线程核心技术>与<java并发编程实战>等相关资料,希望站在巨人的肩膀上,再通过我 ...
- java多线程系列(六)---线程池原理及其使用
线程池 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量的并发访问 java多线程系列(三)之等待通知 ...
- Java多线程系列--“JUC线程池”01之 线程池架构
概要 前面分别介绍了"Java多线程基础"."JUC原子类"和"JUC锁".本章介绍JUC的最后一部分的内容——线程池.内容包括:线程池架构 ...
随机推荐
- Good Triple CodeForces - 1169D (等差子序列)
大意: 给定01字符串, 求有多少个区间$[l,r]$, 使得存在正整数$x,k$满足$1\le x,k\le n,l\le x<x+2k\le r,s_x=s_{x+k}=s_{x+2k}$. ...
- 作业5:Java编译原理
零.编译 1.编译器 (1)前端编译器:.java文件转变为.class文件Sun的javacEclipse JDT中的增量编译器(ECJ) (2)后端编译器:.class文件转变为机器码HotSpo ...
- 作业2:java内存模型图示
参考:http://www.infoq.com/cn/minibooks/java_memory_model?utm_source=infoq&utm_campaign=user_page&a ...
- dev GridView 的组计和分组计
/// <summary> /// //添加组计 /// </summary> private void SetGroupSummary(GridView gv, string ...
- openlayers加载天地图过程中遇到跨域问题
// 采用openlayers加载天地图 var layer = new ol.layer.Tile({ source: new ol.source.XYZ({ // crossOrigin: 'An ...
- yii2-cache组件第三个参数Dependency $dependency的作用浅析
用法如下: $cache->set($key, $result, Configs::instance()->cacheDuration, new TagDependency([ 'tags ...
- Centos7下查看端口占用
netstat -nap #会列出所有正在使用的端口及关联的进程/应用 netstat -lnp|grep 5000 这条语句的作用是查询占用5000端口的应用和进程,把5000端口替换成你要过滤的端 ...
- Hadoop2.7.3集群安装scala-2.12.8 和spark2.7
Apache Spark™是用于大规模数据处理的统一分析引擎. 从右侧最后一条新闻看,Spark也用于AI人工智能 spark是一个实现快速通用的集群计算平台.它是由加州大学伯克利分校AMP实验室 开 ...
- 04.ZabbixWEB网站监控
1.Web场景监测概述 1.Web网站中什么是动态网站,什么是静态网站 静态网站:纯静态网站就是服务器的源代码和客户端的源代码一致. 动态网站:比如: <?php phpinfo()?> ...
- Beta冲刺版本第一天
该作业所属课程:https://edu.cnblogs.com/campus/xnsy/SoftwareEngineeringClass2 作业要求地址:https://edu.cnblogs.com ...