线程安全问题
Posted lxzwhite
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程安全问题相关的知识,希望对你有一定的参考价值。
1.线程安全产生的原因
如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。
程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
以电影院卖票为例演示线程安全问题
由三个不同的渠道同时卖100张票
线程任务:
public class MyRunnable implements Runnable { // 卖电影票(共享数据) private int ticket = 100; public void run() { while (true) { if (ticket > 0) { try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "张票"); } } } }
public static void main(String[] args) { //创建线程任务 MyRunnable mr = new MyRunnable(); //创建线程 Thread t0 = new Thread(mr); Thread t1 = new Thread(mr); Thread t2 = new Thread(mr); //执行线程 t0.start(); t1.start(); t2.start(); }
此时执行代码会发现会出现卖第0张和-1张票或者重复卖同一张票的情况,
原因是某个时间多个线程都执行了同一段if语句中的代码,使得全局变量ticket不准确(操作的票可能被另一个线程卖掉了)
所以解决这个问题需要在一个线程在执行线程任务时,其他线程不能执行;这也就变得和单线程一样了。
解决方法:
(1)同步代码块
public class MyRunnable implements Runnable { // 卖电影票(共享数据) private int ticket = 100; private Object obj = new Object();// 保证锁对象的唯一性 public void run() { while (true) { synchronized (obj) { if (ticket > 0) { try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "张票"); } } } } }
注意:用synchronized(锁对象){}时, 锁对象要设为全局变量保证其唯一性;
(2)同步方法
public class MyRunnable2 implements Runnable { // 卖电影票(共享数据) private int ticket = 100; public void run() { while (true) { sale(); } } public synchronized void sale() { // 隐含的锁 this if (ticket > 0) { try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "张票"); } } }
注意:将同步代码块封装成一个方法并用synchronized 修饰,这里不需要指定锁对象,这里的锁对象默认为本类对象this
若果是静态同步方法: 在方法声明上加上static synchronized
public static synchronized void method(){
可能会产生线程安全问题的代码
}
静态同步方法中的锁对象是 类名.class
(3)使用lock接口
public class MyRunnable2 implements Runnable { // 卖电影票(共享数据) private int ticket = 100; private Lock lock = new ReentrantLock();//创建锁对象 public void run() { while (true) { lock.lock(); if (ticket > 0) { try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "张票"); } lock.unlock(); } }
}
其实和同步代码块差不多 ,在创建所对象时用Lock接口的子类创建,用lock()和unlock()方法将可能出现安全的代码包起来
以上是关于线程安全问题的主要内容,如果未能解决你的问题,请参考以下文章
HashMap 和 ConcurrentHashMap 的区别
newCacheThreadPool()newFixedThreadPool()newScheduledThreadPool()newSingleThreadExecutor()自定义线程池(代码片段