以卖票的例子来介绍多线程和资源共享。
卖票是包含一系列动作的过程,有各种操作,例如查询票、收钱、数钱、出票等,其中有一个操作是每次卖掉一张,就将总的票数减去1。有10张票,如果一个人卖票,先做查票、收钱、数钱等各种操作,再将总的票数减去1,效率很低。如果多个人卖票,每个人都是做同样的操作,数钱、检查钱,最后将总的票数减1,这样效率高。但是有一个问题,如果出现两个人同时将总的票数减掉了1,例如,A、B两个人同时读取到票的总数是10,A从中减去1,同时B也从中减去1,总数显示是9,其实票只有8张。导致数据错误。
按照正常逻辑,同一时刻只允许一个人来从总票数中减去1,A读取总票数,再减去1的过程中,B必须等待,等A操作完了,B才能进行。其实票就是共享资源,一次只能由一个人访问。这里就要用到同步机制,即锁机制,使用关键词synchronized将读取总的票数,并减去1的操作锁定,使得一次只能由一个人访问。每个售票员就是一个线程,多个售票员进行同一项卖票任务。
synchronized原理是,执行synchronized部分代码的时候必须需要对象锁,而一个对象只有一个锁,只有执行完synchronized里面的代码后释放锁,其他线程才可以获得锁,那么就保证了同一时刻只有一个线程访问synchronized里面的代码。使得资源共享的关键是,只有一个实例,synchronized使用的是同一把锁,用实例的锁或者定义一个实例。这就需要使用实现Runnable接口的方式,实现多线程,这样传入的是一个实例。继承Thread的方式,传入的是多个实例,每个实例都有一个锁,那就无法实现控制。代码如下:
public class SellTicketsThread implements Runnable{
private int ticketCount = 50;
Object lockObject = new Object();
public SellTicketsThread() {
}
@Override
public void run() {
while(ticketCount > 0){
sellTicket();
/**
当前线程休眠,好让其他线程继续执行
*/
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void sellTicket(){
synchronized(lockObject){
if(ticketCount > 0){
System.out.println(Thread.currentThread().getName()+"正在卖第:"+ticketCount+"张票"+
",还剩"+(ticketCount-1)+"张票");
ticketCount--;
} else{
System.out.println("票已卖完");
return;
}
}
}
}
主函数调用:
public class SellTicketsMain {
public SellTicketsMain() {
}
public static void main(String[] args) {
SellTicketsThread sellTicketsThread = new SellTicketsThread();
Thread firstThread = new Thread(sellTicketsThread, "线程1");
Thread secondThread = new Thread(sellTicketsThread, "线程2");
Thread thirdThread = new Thread(sellTicketsThread, "线程3");
Thread fourthThread = new Thread(sellTicketsThread, "线程4");
Thread fifthThread = new Thread(sellTicketsThread, "线程5");
firstThread.start();
secondThread.start();
thirdThread.start();
fourthThread.start();
fifthThread.start();
}
}
执行结果:
线程1正在卖第:50张票,还剩49张票
线程2正在卖第:49张票,还剩48张票
线程3正在卖第:48张票,还剩47张票
线程4正在卖第:47张票,还剩46张票
线程5正在卖第:46张票,还剩45张票
线程4正在卖第:45张票,还剩44张票
线程2正在卖第:44张票,还剩43张票
线程1正在卖第:43张票,还剩42张票
线程3正在卖第:42张票,还剩41张票
线程5正在卖第:41张票,还剩40张票
线程5正在卖第:40张票,还剩39张票
线程4正在卖第:39张票,还剩38张票
线程2正在卖第:38张票,还剩37张票
线程1正在卖第:37张票,还剩36张票
线程3正在卖第:36张票,还剩35张票
线程5正在卖第:35张票,还剩34张票
线程3正在卖第:34张票,还剩33张票
线程1正在卖第:33张票,还剩32张票
线程2正在卖第:32张票,还剩31张票
线程4正在卖第:31张票,还剩30张票
线程5正在卖第:30张票,还剩29张票
线程3正在卖第:29张票,还剩28张票
线程1正在卖第:28张票,还剩27张票
线程2正在卖第:27张票,还剩26张票
线程4正在卖第:26张票,还剩25张票
线程2正在卖第:25张票,还剩24张票
线程5正在卖第:24张票,还剩23张票
线程3正在卖第:23张票,还剩22张票
线程1正在卖第:22张票,还剩21张票
线程4正在卖第:21张票,还剩20张票
线程4正在卖第:20张票,还剩19张票
线程2正在卖第:19张票,还剩18张票
线程5正在卖第:18张票,还剩17张票
线程3正在卖第:17张票,还剩16张票
线程1正在卖第:16张票,还剩15张票
线程1正在卖第:15张票,还剩14张票
线程4正在卖第:14张票,还剩13张票
线程2正在卖第:13张票,还剩12张票
线程5正在卖第:12张票,还剩11张票
线程3正在卖第:11张票,还剩10张票
线程2正在卖第:10张票,还剩9张票
线程3正在卖第:9张票,还剩8张票
线程5正在卖第:8张票,还剩7张票
线程1正在卖第:7张票,还剩6张票
线程4正在卖第:6张票,还剩5张票
线程3正在卖第:5张票,还剩4张票
线程5正在卖第:4张票,还剩3张票
线程1正在卖第:3张票,还剩2张票
线程4正在卖第:2张票,还剩1张票
线程2正在卖第:1张票,还剩0张票