线程安全问题的三种解决方案
发布日期:2022-02-01 14:28:20
浏览次数:34
分类:技术文章
本文共 4440 字,大约阅读时间需要 14 分钟。
关于线程安全问题:
1.什么是线程安全问题?.为什么会出现线程安全问题? 首先我们需要了解一个概念,临界资源:即多个线程同时访问的资源(共享资源)。 当多个线程同时操作临界资源的时候,就容易出现线程安全问题,线程安全问题只会影响到线程对同一个共享的全局变量的写操作。
》接下来来演示一下出现线程安全问题的案例:窗口售票
public class Ticket implements Runnable{//创建对象继承Runnable接口 作为参数传入Thread对象构造中 private int ticket=100;//设定临界资源100张票 @Override public void run() { while (true){//死循环 if(ticket<=0){票数少于等于0结束循环 不再买票 break; } try { Thread.sleep(10);//休眠只是为了更明显突出多线程安全问题 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"卖出了第"+ticket+"张票");//打印卖票 ticket--;//每卖出一张票 票数减一 } } }public class TirketDemo { public static void main(String[] args) { Ticket ticket=new Ticket();//创建票对象 new Thread(ticket,"窗口一").start();//作为参数传入 new Thread(ticket,"窗口二").start(); new Thread(ticket,"窗口三").start(); new Thread(ticket,"窗口四").start(); }}
运行结果:
可以看出来不仅出现重复,还有出现负数票的情况,这是因为在线程运行时候,刚判定结束后,cpu倍另外的线程抢夺而出现的问题,再次醒来之后,票数已经被其他线程修改,该线程仍然继续向下执行减一 就有可能出现负数或者重复。解决方法大致有三种:一同步代码块
说到同步代码块就要说锁了,一个线程在访问临界资源的时候,如果给这个资源“上一把锁”,这个时候如果其他线程也要访问这个资源,就得在“锁”外面等待。
锁对象可以是任意对象,但是要求必须是同一把锁才行。 加进同步代码块的代码,执行时候其他线程必须等待,待当前进入的线程执行完,释放锁对象之后其他线程才能进去执行,以此类推。 同步中的线程没有执行完毕不会释放锁,同步外的线程没有锁对象,进不去同步
代码实现:加了同步代码块之后就不会出现以上问题
public class Ticket implements Runnable{ private int ticket=100; @Override public void run() { while (true){ synchronized (this) {//在这里加上同步代码块并传入所对象 if(ticket<=0){ break; } try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"卖出了第"+ticket+"张票"); ticket--; } } }
二.同步方法
创建一个方法,该方法必须用synchronized关键字修饰,将出现安全问题的代码放入到同步方法中,再在Runnable实现类重写的run方法中调用该方法即可。
同步方法默认的锁对象是调用该方法的类 也就是this
public class Ticket implements Runnable{ private int ticket=100; @Override public void run() { while (true) { sellTicket(); } } public synchronized boolean sellTicket(){//注意:while循环不能加到方法里面 因为这样的话 调用一次方法 一个线程就会把票卖完了 这里我们返回boolean来判定 if(ticket<=0){//票数少于等于0结束循环 不再买票 return false; } try { Thread.sleep(10);//休眠只是为了更明显突出多线程安全问题 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"卖出了第"+ticket+"张票");//打印卖票 ticket--;//每卖出一张票 票数减一 return true; }
》静态同步方法:即在方法加静态修饰符,静态方法的锁不能是this,因为this只有在类加载之后创建对象才会有,而静态方法在创建对象之前就已经加载,所以同步静态方法的锁是本类的class
public class Ticket implements Runnable{ private static int ticket=100;//静态方法只能访问静态变量 @Override public void run() { while (true) { sellTicket(); } } public synchronized static boolean sellTicket(){//注意:while循环不能加到方法里面 因为这样的话 调用一次方法 一个线程就会把票卖完了 if(ticket<=0){//票数少于等于0结束循环 不再买票 return false; } try { Thread.sleep(10);//休眠只是为了更明显突出多线程安全问题 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"卖出了第"+ticket+"张票");//打印卖票 ticket--;//每卖出一张票 票数减一 return true; }
三.Lock锁
从jdk1.5之后加入新的接口 Lock,ReentrantLock是Lock接口的实现类。
通过显式定义同步锁对象来实现同步,同步锁提供了比synchronized代码块更广泛的锁定操 注意:最好将 unlock的操作放到finally块中 通过使用ReentrantLock这个类来进行锁的操作,它实现了Lock接口,使用ReentrantLock可以显式地加锁、释放锁。
lock锁使用步骤:1.在成员位置创建一个Reentrantlock对象,因为lock是一个接口需要实现类才能创建对象。
2.在可能会出现安全问题的代码前调用接口方法中的lock()方法获取锁 3.在可能会出现安全问题的代码后面调用接口方法中的Unlock()方法释放锁 注意:最好将有安全问题的代码放到try…finally{ … }中,finally中用于释放锁。
代码:
public class Ticket implements Runnable{ private int ticket=100; Lock lock=new ReentrantLock();//创建锁对象 @Override public void run() { while (true){ lock.lock();//获取锁 try { if(ticket<=0){ break; } System.out.println(Thread.currentThread().getName()+"卖出了第"+ticket+"张票"); ticket--; } catch (Exception e) { e.printStackTrace(); }finally { lock.unlock();//释放锁 } } }
转载地址:https://blog.csdn.net/shunshizhen120412/article/details/99185893 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
第一次来,支持一个
[***.219.124.196]2024年03月27日 20时17分52秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
Word2vec与logging模块_完整代码_CodingPark编程公园
2019-04-26
Auto-Encoder&VAE_完整代码_CodingPark编程公园
2019-04-26
Debug讲解_CodingPark编程公园
2019-04-26
命名实体识别_利用CRF_CodingPark编程公园
2019-04-26
Pycharm使用-取消pytest的办法_CodingPark编程公园
2019-04-26
OpenCV图像处理基础_CodingPark编程公园
2019-04-26
生成个性二维码_CodingPark编程公园
2019-04-26
读txt文件报错_CodingPark编程公园
2019-04-26
文件读&写_Python_CodingPark编程公园
2019-04-26
list与str的转换&list嵌套问题_CodingPark编程公园
2019-04-26
Gensim库生成与导入W2V模型_CodingPark编程公园
2019-04-26
cmd命令行实用技巧_CodingPark编程公园
2019-04-26
数据可视化与文本分类_CodingPark编程公园
2019-04-26
数据分析之数据可视化_CodingPark编程公园
2019-04-26
隐马尔可夫模型的样本生成&模型训练&预测_CodingPark编程公园
2019-04-26