引入
Java多线程同步中有一个非常经典的问题就是火车站卖票问题,通过实现Runnable接口,实现run方法,然后通过Thread创建多个线程表示窗口共享一个runnable对象的资源来模拟卖票。
- 售票窗口类:
/**
* @author lzp
* @Date 2020年7月25日
*/
public class Ticket implements Runnable {
private int ticket = 200;// 火车票
@Override
public void run() {
while (ticket > 0) {
try {
Thread.sleep(100); //为了暴露问题 可不加
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖了第" + ticket-- + "张票");
}
}
}
- 测试类,创建四个窗口进程开始卖票
/**
* 1、重写四个窗口卖200张火车票,两种同步方式
* @author lzp
* @Date 2020年7月25日
*/
public class Test1 {
public static void main(String[] args) {
Ticket ticket = new Ticket();
Thread thread1 = new Thread(ticket,"窗口一");
Thread thread2 = new Thread(ticket,"窗口二");
Thread thread3 = new Thread(ticket,"窗口三");
Thread thread4 = new Thread(ticket,"窗口四");
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
- 结果如下,可以看到四个窗口卖了许多重复的票,甚至还有卖了第0张票的情况,这就是因为几个线程抢占资源,几乎同时卖票导致的问题。
解决
通过加同步锁可以解决这个问题,通过加锁,一个线程在使用资源期间,其他线程必须等待资源释放后才能使用,这样就能避免几个窗口同时卖一张票的情况。通过synchronized关键字加锁有两种方式,分为同步代码块和同步方法。
同步代码块
将原来的窗口代码修改如下:
/**
* @author lzp
* @Date 2020年7月25日
*/
public class Ticket implements Runnable {
private int ticket = 200;// 火车票
@Override
public void run() {
while (ticket > 0) {
//同步代码块
synchronized (this) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "卖了第" + ticket-- + "张票");
}
}
}
}
}
这样运行后就不会出现多个窗口卖同一张票的情况了。
同步方法
与同步代码块的方法相似只不过锁修饰在方法上:
/**
* @author lzp
* @Date 2020年7月25日
*/
public class Ticket implements Runnable {
private int ticket = 200;// 火车票
@Override
public void run() {
while (ticket > 0) {
saleTicket();
}
}
//同步方法
public synchronized void saleTicket() {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "卖了第" + ticket-- + "张票");
}
}
}