问题描述
如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样
的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
我们通过一个案例,演示线程的安全问题:
电影院要卖票,我们模拟电影院的卖票过程。假设要播放的电影是 “葫芦娃大战奥特曼”,本次电影的座位共100个
(本场电影只能卖100张票)。
我们来模拟电影院的售票窗口,实现多个窗口同时卖 “葫芦娃大战奥特曼”这场电影票(多个窗口一起卖这100张票)
- 相同的票数,比如5这张票被卖了多回。
- 不存在的票,比如0票与-1票,是不存在的。
这种问题,几个窗口(线程)票数不同步了,这种问题称为线程不安全。
线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。
解决方案:
1 . 线程同步代码块
public class Ticket implements Runnable{
private int ticket = 100;
Object lock = new Object();
/*
* 执行卖票操作
*/
@Override
public void run() {
//每个窗口卖票的操作
//窗口 永远开启
while(true){
synchronized (lock) {
if(ticket>0){//有票 可以卖
//出票操作
//使用sleep模拟一下出票时间
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto‐generated catch block
e.printStackTrace();
}
//获取当前线程对象的名字
String name = Thread.currentThread().getName();
System.out.println(name+"正在卖:"+ticket‐‐);
}
}
- 同步方法 `
public class Tickets implements Runnable {
private int tickets = 100;
@Override
public void run() {
while (true) {
getTicket();
}
}
public synchronized void getTicket(){//,窗口3(排队)
if(tickets > 0){//窗口2(排队)
System.out.println(Thread.currentThread().getName() + " 来取走一张票:" + tickets);
tickets--;
}else{
System.out.println("没票了,都回去吧!");
System.exit(0);
}
}
//窗口1(释放锁)
}
public class Demo {
public static void main(String[] args) {
Tickets ts = new Tickets();
Thread t1 = new Thread(ts);
Thread t2 = new Thread(ts);
Thread t3 = new Thread(ts);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
- lock锁
public class Tickets implements Runnable {
private int tickets = 100;
private Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();//加锁//窗口2(排队),窗口3(排队)
try {
if (tickets > 0) {//窗口1(立即锁门)
System.out.println(Thread.currentThread().getName() +
" 来取走一张票:" + tickets);
tickets--;
} else {
System.out.println("没票了,都回去吧!");
break;
}
}finally {
lock.unlock();//解锁//窗口1(释放锁)
}
}
}
}
public class Demo {
public static void main(String[] args) {
Tickets ts = new Tickets();
Thread t1 = new Thread(ts);
Thread t2 = new Thread(ts);
Thread t3 = new Thread(ts);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}