上代码:
java">public class Test {
public static void main(String[] args) {
ThreadTicket ticket = new ThreadTicket();
Thread t1 = new Thread(ticket);
Thread t2 = new Thread(ticket);
t1.start();
t2.start();
}
}
class ThreadTicket implements Runnable {
int ticket = 10;
@Override
public void run() {
while(true) {
if(ticket>0) {
try {
Thread.currentThread();
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖。。。"+ticket--);
} else {
break;
}
}
}
}
运行结果:
java">Thread-1卖。。。10
Thread-0卖。。。10
Thread-0卖。。。9
Thread-1卖。。。8
Thread-1卖。。。7
Thread-0卖。。。6
Thread-0卖。。。4
Thread-1卖。。。5
Thread-0卖。。。3
Thread-1卖。。。2
Thread-1卖。。。1
Thread-0卖。。。0
问题:偶尔会出现重复卖票的情况,显示这在开发中是不允许的
原因分析:java中的原子性操作,是指读和写是原子性的,比如 i=5,这就是一个原子性的操作。多线程执行的时候,只能在一个原子操作力,才会没有并发读或并发写的情况。
通常情况下,在java里面,i++或者i--不是线程安全的,这里面有三个独立的操作:获得变量当前的值,为该值+1或者-1,然后写回新的值。
在没有额外资源可以利用的情况下,只能使用加锁才能保证读-改-写这三个操作是原子性的,这就是为什么会出现一定数量重复卖票的情况
另外,sleep()中指定的时间是线程不会运行的最短时间,因此,sleep()方法不能保证该线程睡眠到期后就开始执行,所以,加上sleep()以后,就增加了并发操作ticket()的机会,出现重复卖票的可能性也会增加
修改后代码:
java">class ThreadTicket implements Runnable {
int ticket = 10;
@Override
public void run() {
synchronized (this) {
while(true) {
if(ticket>0) {
try {
Thread.currentThread();
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖。。。"+ticket--);
} else {
break;
}
}
}
}
}
运行结果:
java">Thread-0卖。。。10
Thread-0卖。。。9
Thread-0卖。。。8
Thread-0卖。。。7
Thread-0卖。。。6
Thread-0卖。。。5
Thread-0卖。。。4
Thread-0卖。。。3
Thread-0卖。。。2
Thread-0卖。。。1