线程安全
当多个线程同时分别执行同一个线程时,可能出现安全问题。比如电影院三个售票窗口,同时开始售同样的100张票,就可能出现两个窗口同时售同一张票,或者某个窗口售卖不存在的票的问题。
代码示例:
public class ThreadSafeClass implements Runnable{
public int ticket=100;
@Override
public void run() {
while(ticket>0) {
System.out.println(Thread.currentThread().getName() + "第 "+ ticket+"张票");
ticket--;
}
}
}
public class ThreadSafeTest {
public static void main(String[] args) {
ThreadSafeClass ts = new ThreadSafeClass();
Thread t0 = new Thread(ts);
Thread t1 = new Thread(ts);
Thread t2 = new Thread(ts);
t0.start();
t1.start();
t2.start();
}
}
输出结果:
可见前三次就输出了同样的第100张票。 即第100张票被重复卖了。这就出现了线程安全问题。
这里针对前面四次输出进行分析。
当t0,t1,t2三个线程同时开始执行,并且同时执行到System.out.println,此时ticket还等于100,并且没有来得及进行自减。 所以t0和t2都输出了第100张票。而t1显然输出速度明显慢于t0和t2。
解决线程安全问题
运行线程的同步技术来解决线程安全问题。
同步代码块synchroized
针对卖票案例出现的线程安全问题, 卖出了不存在的票和重复的票
解决线程安全问题的一种方案:使用同步代码块
格式:
synchronized(锁对象){
可能会出现线程安全问题的代码(访问了共享数据的代码)
}
注意:
1.通过代码块中的锁对象,可以使用任意的对象
2.但是必须保证多个线程使用的顾对象是同一个
3.锁对象作用:
把同步代码块锁住,只让-一个线程在同步代码块中执行
对刚才的售票代码进行如下修改
public class ThreadSafeClass implements Runnable{
public int ticket=100;
//创建一个锁对象
Object obj = new Object();
//创建一个线程任务,卖票
@Override
public void run() {
while(ticket>0) {
synchronized(obj) {
System.out.println(Thread.currentThread().getName() + "第 "+ ticket+"张票");
ticket--;
}
}
}
}
第二种方法 同步方法
解决线程安全问题的二种方案:使用同步方法
使用步骤:
- 把访问了共享数据的代码抽取出来,放到一个方法中
- 在方法上添加synchronized修饰符
格式:定义方法的格式
修饰行 synchronized 近回值类型方法名(参数列表){
可能会出现线程安全问题的代码(访问了共享数据的代码)
}