线程运行往往不尽人意
问题:一共有四个售票机,总票数为100.设计程序用多线程解决问题.每个线程代表一个售票机.
乍一看感觉这个题目非常简单,只要设计四个线程,每个线程用while执行,在while中加上线程锁,就可以解决问题.
这是启动程序:
主要功能是建立Thread对象,然后设置四个线程,同时执行售票程序.
这是启动程序:
主要功能是建立Thread对象,然后设置四个线程,同时执行售票程序.
package package_1;
public class Main_Thread {
public static void main(String[] args) {
Ticket_Thread st = new Ticket_Thread();
Thread t1 = new Thread(st, "售票机一号");
Thread t2 = new Thread(st, "售票机二号");
Thread t3 = new Thread(st, "售票机三号");
Thread t4 = new Thread(st, "售票机四号");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
这是子程序:
package package_1;
public class Ticket_Thread implements Runnable {
private int count = 1;
private Object lock = new Object();
@Override
public void run() {
Thread thisThread = Thread.currentThread();
while (count <= 100) {
synchronized (lock) {
System.out.println("本次为您服务的是" + thisThread.getName() + "售票机:");
System.out.println("已出售" + count + "张,剩余:" + (100 - count) + "张");
count++;
}
}
}
}
我们预计的结果是每条线程随机分配执行,当count = 100的时候,循环条件被打破,各个子线程执行完毕,同时主线程也执行完毕.
以上代码的执行结果:
预期和结果不符,为什么售票数会显示-1~-3呢?
先让我们来看一下线程执行的流程图(图片)
从start开始,等分配到了cpu资源之后,每一条线程就开始执行自己的run()了,当在run()中遇到synchronized()时,每条线程进入
等待状态(等待池),随机获得锁.当synchronized块执行完毕之后,归还锁.如果满足循环条件,循环继续.
猜想1:问题主要出在最后几个结果.我们设置用count来记录次数,当count = 10的时候,while循环结束.由于synchronized是包含在while中的,如果当每个线程同时执行(时间差为0)的时候,理论上每个线程在执行完synchronized(获取锁,归还锁)之后会同步执行.
对于我们的程序来,当count = 10时,所有线程进入while循环,继续往下执行,遇到synchronized,其中一条线程拿到锁,继续执行,其他线程进入等锁池.当这条线程执行完synchronized块之后 归还锁,使得count++,这时候count = 11,这一条线程条件不符合,循环结束.
但是其它三条线程已经处于while中,无法在此时判断条件,所以继续随机获得锁,继续执行.由此类推,可以得出当线程1执行了count++,使count = 11之后,其余个线程还要执行一次synchronized中的代码.最后使得售票数量为-3,此时的count应该为13.
我们可以验证一下,在执行完循环之后,输出count
到这里问题似乎解决了,但加上加上Thread.sleep()之后,结果却变成了这样:
设置休眠1毫秒
public void run() {
Thread thisThread = Thread.currentThread();
while (count <= 100) {
synchronized (lock) {
System.out.println("本次为您服务的是" + thisThread.getName() + "售票机:");
System.out.println("已出售" + count + "张,剩余:" + (100 - count) + "张");
count++;
}
try {
Thread.sleep(0);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
设置休眠10毫秒:
按照我们刚刚的猜想,如果每个线程正常运行的话,结果都应该使 -3 才对,可为什么当线程休眠了1毫秒的时候会出现那么多结果呢?
原来每个线程交替执行,cpu的交替调用也是需要时间的.count自增需要时间,归还锁需要时间,while()条件的判断也是需要时间的,总的来说,各种操作都需要花费时间.当线程1比线程2多执行一次,那么完成自增的count = 11的时候,线程2就有可能还是处于休眠状态时,当此线程休眠结束,再次判断循环条件的时候,count已经等于11了,循环终止,count也就不再自增了,如果不处于休眠状态的时候,说明已经进入while循环,这时候count还是会自增.这取决于线程交替调用的时间消耗.时间消耗大于另一条线程的休眠时间时,便会进入下一个循环.由于计算机每次执行所消耗的时间不同,得出的结论也会有所不同.
为了验证这一点,我们把交替次数减少,让休眠时间增加.
public void run() {
Thread thisThread = Thread.currentThread();
while (count <= 10) {
synchronized (lock) {
System.out.println("本次为您服务的是" + thisThread.getName() + "售票机:");
System.out.println("已出售" + count + "张,剩余:" + (10 - count) + "张");
count++;
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
这时候结果为:
简单分析一下,我们使得每次交替的时间小于休眠的时间,使得每次循环的时候,每条线程都会执行一次.最后得出结果为-2.
当然不让负票数出现的另一个方法,也是很简单的方法就是:
public void run() {
Thread thisThread = Thread.currentThread();
while (count <= 100) {
synchronized (lock) {
if (count == 101) {
System.out.println(thisThread.getName() + ":已售完!!");
break;
}
System.out.println("本次为您服务的是" + thisThread.getName() + "售票机:");
System.out.println("已出售" + count + "张,剩余:" + (100 - count) + "张");
}
System.out.println("count:" + count);
}
}
在synchronized块中加一个分支条件判断呗~