线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。也可以说线程安全问题,是因为访问了共享的数据。

模拟一个卖票过程,假设总共有100张票,有三个售票员(开启三个线程)



public class SaleTickets implements Runnable {
private int tickets = 100;
@Override
public void run() {
// 让其不断的卖票
while(true) {
// tickets必须大于0
if (tickets > 0) {
// 取到票后,就休息10毫秒
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 打印出卖票信息
System.out.println(Thread.currentThread().getName() + "正在卖第" + tickets + "票");
// 票数实现-1
tickets--;
}
}
}
}


实现卖票



public class TestSale {
public static void main(String[] args) {
// 开启三个新线程,模拟三个售票员
SaleTickets st = new SaleTickets();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
Thread t3 = new Thread(st);
t1.start();
t2.start();
t3.start();
}
}


控制台打印出的结果:

线程安全问题_静态变量

线程安全问题_静态变量_02

 

 

 发现同一张票100被卖出了三次甚至是卖出不存在的票0,这明显是错误的。这时就出现线程安全问题。

原因:

  1.卖出三张100号的票,是因为三个线程同时执行了

System.out.println(Thread.currentThread().getName() + "正在卖第" + tickets + "票");


且三个都线程都执行了tickets--,所以就没有99、98,而是直接跳转到了97。

  2.卖出了0号票,是因为当Thread-0获得CPU的使用权进入run()方法时,然后由于程序调用了sleep()方法,故Thread-0进入睡眠;此时Thread-1获得CPU的使用权,进入run()方法,然后又sleep()睡眠了,失去了CPU的使用权;此时,Thread-2获得CPU的使用权,进入run()方法,执行到sleep(),进入睡眠。然后Thread-0睡醒了,开始输出语句,并且执行tickets--;此时tickets=0,且0>0不成立,故Thread-0线程就退出了;故Thread-1此时获得CPU,开始输出语句,此时tickets=0,故输出了0号票。