当前位置:网站首页>线程安全问题以及其解决方法
线程安全问题以及其解决方法
2022-08-02 14:32:00 【naoguaziteng】
目
录
1.为什么会出现线程安全问题?
实例代码:
public class MyTest {
public static void main(String[] args) {
SellTicketRunnable runnable = new SellTicketRunnable();
//创建了三个线程
Thread th1 = new Thread(runnable);
Thread th2 = new Thread(runnable);
Thread th3 = new Thread(runnable);
//给三个线程重新命名
th1.setName("窗口1");
th2.setName("窗口2");
th3.setName("窗口3");
//开启线程
th1.start();
th2.start();
th3.start();
}
}
public class SellTicketRunnable implements Runnable {
static int num = 100; //三个线程共享了这个数据(一共有100张票)
@Override
public void run() {
while (true) {
if (num > 0) {
try {
Thread.sleep(20); //模拟以下延迟
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-正在出售" + (num--) + "张票");
}
}
}
}
现实中售票时网络是不能实时传输的,总是存在延迟的情况,所以,在出售一张票以后,需要一点时间的延迟.于是在代码中加入了一点延迟!这个不影响什么的!看起来没有什么问题!部分运行结果如下:
看到这运行结果,就发现问题了!现实中的售票情况怎么可能会几个窗口出售同一张票呢?甚至有的时候还会出现出售0票和负票的情况.这就是出现了线程安全问题!
那么重点来啦!为什么会出现线程安全问题呢?为什么会几个窗口出售同一张票,0票甚至负票呢?请看图!!!!!!
如上这样毫无规矩,随机性的抢占,当然会坏事啦!试想一下,去医院看病,这般你争我抢,直接毁灭吧!!!说个玩笑话!这样的抢占,就引起了安全问题!怎么解决,相信友友们已经有想法了.
2.判断一个多线程应用程序是否有问题的标准:
总结一下会出现安全隐患的标准: 得是多线程环境、有共享数据、有多条语句操作共享数据!
3.如何解决多线程安全问题呢?
我们无法让其没有共享数据更没有办法让其不处于多线程的环境!如果不是多线程,那当然不会出现安全隐患的问题啦!那么只能从多条语句操作共享数据下手了!那在操作共享数据的时候,能不能一次只让一个进入呢?当然可以!所以就出现了锁! 把多个语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可,操作完了,锁立马释放掉! 就不会出现线程安全问题啦!
4.同步代码块(synchronized)
同步代码块的格式:
synchronized(对象){
要被同步的代码 ;
}
注意: 不能在大括号里直接new 对象, new 了 就没效果了!
这个同步代码块保证数据的安全性的一个主要因素就是这个对象. 注意这个对象要定义为静态成员变量才能被所有线程共享.这个对象其实就是一把锁. 这个对象习惯叫做监视器.
锁的分类:
内置锁
每个java对象都可以用做一个实现同步的锁,这些锁便被称为内置锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。java内置锁是一个互斥锁. 同步代码代码块上的锁亦是如此. 这就是意味着最多只有一个线程能够获得该锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,直到线程B释放这个锁,如果B线程不释放这个锁,那么A线程将永远等待下去。
对象锁和类锁
java的对象锁和类锁在锁的概念上基本上和内置锁是一致的. 但是两个锁实际是有很大区别的,对象锁是用于对象实例方法或者一个对象实例上的,而类锁是用于类的静态方法或者一个类的class对象上的。类的对象实例可以有很多个,但是每个类只有一个class对象,所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。还有一点必须注意的哟!也就是类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的.
哈哈哈!不要晕乎乎!这个锁的分类只是想让友友们了解一下而已啦!
用锁解决案例问题:
我们用锁解决一下上面案例出现的线程安全问题!!!!
public class MyTest {
public static void main(String[] args) {
SellTicketRunnable runnable = new SellTicketRunnable();
//创建了三个线程
Thread th1 = new Thread(runnable);
Thread th2 = new Thread(runnable);
Thread th3 = new Thread(runnable);
//给三个线程重新命名
th1.setName("窗口1");
th2.setName("窗口2");
th3.setName("窗口3");
//开启线程
th1.start();
th2.start();
th3.start();
}
}
public class SellTicketRunnable implements Runnable {
static int num = 100; //三个线程共享
static Object obj = new Object(); //上锁对象,静态成员变量
@Override
public void run() {
while (true) {
//th1 th2 th3中任意一个线程,一旦进入同步代码块,就会被加锁,其他没被加锁的对象便会在此等待
synchronized (obj) { //这个就是锁
if (num > 0) {
try {
Thread.sleep(20); //模拟延迟
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-正在出售" + (num--) + "张票");
}
} //出了同步代码块,就会释放锁
}
}
}
现在查看结果就不会出现几个窗口出售同一张票以及出售0票和负票的情况了!!!!这可是个好东西啊!!!
同步的好处和弊端:
同步的出现 解决了多线程的安全问题。 其弊端就是,当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会 降低程序的运行效率。
5.死锁问题
如果出现了同步嵌套,就会很容易产生死锁问题.是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象.简单来说死锁是 两个或者两个以上的线程,在抢占CPU的执行权的时候,都处于等待状态.
public class MyThread extends Thread {
private boolean flag;
public MyThread(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (flag) {
synchronized (LockUtils.objA) {
System.out.println("true线程进来了持有ObjA锁");
synchronized (LockUtils.objB) {
System.out.println("true线程进来了持有ObjB锁");
}
}
} else {
synchronized (LockUtils.objB) {
System.out.println("false线程进来了持有ObjB锁");
synchronized (LockUtils.objA) {
System.out.println("false线程进来了持有ObjA锁");
}
}
}
}
}
public interface LockUtils {
//定义了两把锁对象
Object objA = new Object();
Object objB = new Object();
}
public class MyTest {
public static void main(String[] args) throws InterruptedException {
//创建了两个线程
MyThread th1 = new MyThread(true);
MyThread th2 = new MyThread(false);
//开启线程
th1.start();
th2.start();
}
}
运行结果便是如此!你等我,我等你.害!孽缘啊!所以一定要注意切勿造成如此"孽缘"!!!!
(小编也在努力学习更多哟!以后再慢慢分享的啦!)
希望对友友们有所帮助!!!!
边栏推荐
- PAT tree DP (memory search) class a, 1079, 1090, 1106
- codeforces k-Tree (dp still won't work)
- 一文让你快速写上扫雷游戏!童年的经典游戏,发给你的小女友让你装一波!!
- 第六章-6.1-堆-6.2-维护堆的性质-6.3-建堆
- “绿色低碳+数字孪生“双轮驱动,解码油气管道站升级难点 | 图扑软件
- 2022-07-19 第五小组 瞒春 学习笔记
- 自定义属性
- 什么是Nacos?
- Drag and Drop in H5
- How to check the WeChat applet server domain name and modify it
猜你喜欢
随机推荐
codeforces Linova and Kingdom
已解决ModuleNotFoundError: No module named‘ pip‘(重新安装pip的两种方式)
状态码以及访问百度过程
Servlet基础详解
初入c语言
散列表简述
自定义属性
How to check the WeChat applet server domain name and modify it
一文让你快速写上扫雷游戏!童年的经典游戏,发给你的小女友让你装一波!!
第三章-函数的增长-3.1-渐近记号
js电梯导航基础案例
PAT Class A 1145 Hash - Average Lookup Time
职工管理系统(SSM整合)
一文让你快速手写C语言-三子棋游戏
XML技术
【go-zero】go-zero 框架踩坑指南 Q&A (持续更新中)
双亲委派机制
Traverse Heap PAT Class A 1155 Heap Path
lambda表达式、Stream接口及Optional类
2022-07-09 第五小组 瞒春 学习笔记