模拟订票场景,总共10张票,但是有20个人想要购买,所以定义了一个订票管理类专门负责操作这10张票,当然,操作票的时候使用到了synchronized,另外定义了一个调用订票方法的线程类用于模拟想要买票的人,然后创建20个线程模拟20个人来订票。但是运行测试的结果却并没有成功模拟这10张票的售卖过程,原因和处理分析如下:
订票管理类
设置 10 张票出售。
package com.example.study;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 订票管理
*
* @author 李关钦
* @date 2017年6月7日
*/
public class TicketManager {
public static int ticket = 10;// 默认总共 10 张票
private static final Logger logger = LoggerFactory.getLogger(TicketManager.class);
/**
* 售票,先检查是否有票,有则对原票数减一
*
* @return
*/
public synchronized boolean buyTicket() {
// 延迟 1 秒钟,模拟实际情况中的处理时间
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (ticket > 0) {
logger.info("current thread name is [{}] , before is [{}] , after is [{}]", Thread.currentThread().getName(), ticket, --ticket);
return true;
} else {
logger.info("current thread name is [{}] , ticket is [{}] , The ticket has been sold out !!!", Thread.currentThread().getName(), ticket);
return false;
}
}
}
订票线程
模拟订票的人。
package com.example.study;
/**
* 订票线程,实现 Runnable 接口
*
* @author 李关钦
* @date 2017年6月7日
*/
public class BuyTicketRunnable implements Runnable {
private TicketManager ticketManager = new TicketManager();
@Override
public void run() {
ticketManager.buyTicket();
}
}
测试
创建 20 个线程,模拟
package com.example.study;
/**
*
*
* @author 李关钦
* @date 2017年6月7日
*/
public class Test {
public static void main(String[] args) {
// 设置 20 个进程,模拟 20 个人订票
for (int i = 0; i < 20; i++) {
Thread thread = new Thread(new BuyTicketRunnable());
thread.start();
}
}
}
结果:
从运行结果可以看到,线程 10 和线程 8 订票的结果一样了,也就说明了使用 synchronized 修饰的订票方法不起效,达不到互斥的效果。
原因:
在以上代码中,每创建一个订票线程 BuyTicketRunnabl创建一个 TicketManager 订票管理对象与之对应,也就是说创建了 20 个订票线程就会有 20 个对应的订票管理对象去操作票数,所以达不到互斥的关系。
解决方法:
在订票线程中用 static 修饰订票管理对象,使订票管理对象成为全局变量,与其他线程共享,所以 创建的 20 个线程就会共享同一个 TicketManager 订票管理对象,而 TicketManager 里面的订票方法又是使用 synchronized 的,所以就会达到互斥的效果。
package com.example.study;
/**
* 订票线程,实现 Runnable 接口
*
* @author 李关钦
* @date 2017年6月7日
*/
public class BuyTicketRunnable implements Runnable {
private static TicketManager ticketManager = new TicketManager();
@Override
public void run() {
ticketManager.buyTicket();
}
}