版权声明:转载请注明出处 https://blog.csdn.net/doubleguy/article/details/86748777
用一个案例来说明:假如我们要实现一个售票的小功能,用3个线程售出共2000张票。
初始模型为:
package com.test7;
public class synchronizedTest {
public static void main(String [] args){
TicketWindow tw = new TicketWindow();
Thread t1 = new Thread(tw);
Thread t2 = new Thread(tw);
Thread t3 = new Thread(tw);
t1.start();
t2.start();
t3.start();
}
}
class TicketWindow implements Runnable{
int nums = 2000;
@Override
public void run() {
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(nums>0){
System.out.println(Thread.currentThread().getName()+"当前正在售出弟"+nums+"张票");
nums--;
}
else{
break;
}
}
}
}
代码运行结果为:
但是我们发现这3个线程会出现同时售出一张票的情况,这是怎么回事呢?以售出第1766张票为例,因为这3个线程是多线程并发执行的,当其中一个线程Thread-2访问第1766张票时,进入if语句,执行完
System.out.println(Thread.currentThread().getName()+"当前正在售出弟"+nums+"张票");
这条语句而还未执行nums--时,另一个线程Thread-1也开始访问第1766张票,进入了if语句,开始执行
System.out.println(Thread.currentThread().getName()+"当前正在售出弟"+nums+"张票");
此时便会同时售出两张1766张票。
我们知道,这种线程是极其危险的,那么怎么解决,让线程变得安全呢?
其实很简单,只需要加一个对象锁,什么是对象锁呢?别急,往下看。
如果我们把上面线程并发执行的过程抽象成上厕所的话,就一个马桶,很多个人在外面排队,如果两个或者多个人程同时上一个厕所,就很容易出现问题,这种线程是不安全的。要解决问题只需要在门上安一把锁,必须等上一个人解决了下一个人才能进入,要解决的一个一个来。(扩展一下,会不会有一直解决不了的情况呢?会,比如当一个线程a在A厕所中执行线程,线程b等a解决完了才能进入A厕所,而a执行过程中要调用B厕所,而B厕所正好有人在里面了,这个人正好是B。这样的话,线程A永远结束不了,就出现了死锁问题)
我们解决上述问题的方法就是加个对象锁,对象锁有0和1两种状态,默认情况为1,代表厕所里没人,线程可以进入。一旦线程进入,她就会变成0---有人的状态,其他线程不能进入,不能进入的线程会进入线程等待池中(blocked阻塞状态)。任何对象都可以充当对象锁,一般用this充当对象锁。
代码是
synchronized (Object) {要线程同步的代码块}
最终修改代码为:
package com.test7;
public class synchronizedTest {
public static void main(String [] args){
TicketWindow tw = new TicketWindow();
Thread t1 = new Thread(tw);
Thread t2 = new Thread(tw);
Thread t3 = new Thread(tw);
t1.start();
t2.start();
t3.start();
}
}
class TicketWindow implements Runnable{
int nums = 2000;
@Override
public void run() {
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (this) {
if (nums > 0) {
System.out.println(Thread.currentThread().getName() + "当前正在售出弟" + nums + "张票");
nums--;
} else {
break;
}
}
}
}
}
运行结果为: