文章目录
前言
复习多线程章节的时候有些疑问,想想后,理解的更深了。由此来记录以下。希望也可以帮助其他的人。
简单的原理: 队列+锁
常见的买票案例(线程安全:多个线程操作同一个数据。)
public class TestTicket02 implements Runnable{
//库存中有 100 张票
private int ticket = 100;
//在这个方法中,进行买票
@Override
public void run() {
//怎么买票? 票没就结束
//循环买票
while(true){
// 票没有了,就进行停止
if(ticket <= 0){
break;
}
//有票就进行卖票
System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"票");
//买了一张,就少一张票
ticket--;
}
}
public static void main(String[] args) {
TestTicket02 testTicket02 = new TestTicket02();
new Thread(testTicket02,"1").start();
new Thread(testTicket02,"2").start();
new Thread(testTicket02,"3").start();
}
}
结果:
1买了第100票
3买了第100票
3买了第98票
2买了第100票
3买了第97票
1买了第99票
3买了第95票
2买了第96票
3买了第93票
1买了第94票
3买了第91票
2买了第92票
3买了第89票
1买了第90票
3买了第87票
2买了第88票
3买了第85票
1买了第86票
3买了第83票
2买了第84票
3买了第81票
1买了第82票
3买了第79票
2买了第80票
3买了第77票
1买了第78票
3买了第75票
2买了第76票
3买了第73票
1买了第74票
3买了第71票
2买了第72票
3买了第69票
1买了第70票
3买了第67票
2买了第68票
3买了第65票
1买了第66票
1买了第62票
1买了第61票
1买了第60票
1买了第59票
1买了第58票
1买了第57票
1买了第56票
1买了第55票
1买了第54票
1买了第53票
1买了第52票
1买了第51票
1买了第50票
1买了第49票
1买了第48票
1买了第47票
1买了第46票
1买了第45票
1买了第44票
1买了第43票
1买了第42票
1买了第41票
1买了第40票
1买了第39票
1买了第38票
1买了第37票
1买了第36票
1买了第35票
1买了第34票
1买了第33票
1买了第32票
1买了第31票
1买了第30票
1买了第29票
1买了第28票
1买了第27票
1买了第26票
1买了第25票
1买了第24票
1买了第23票
1买了第22票
1买了第21票
1买了第20票
1买了第19票
1买了第18票
1买了第17票
3买了第63票
2买了第64票
3买了第15票
1买了第16票
1买了第12票
1买了第11票
1买了第10票
1买了第9票
3买了第13票
2买了第14票
3买了第7票
1买了第8票
3买了第5票
2买了第6票
3买了第3票
1买了第4票
3买了第1票
2买了第2票
这里添加Thread.sleap(…) 有不同的结果。
会导致:
1
0
-1
的结果
使用同步代码块或者同步方法(解决线程不安全的问题)
添加的地方不同,会有不同的结果。
添加的方式一:synchronized 关键字
由于只进行修改run()中的代码所有,只显示这部分代码
@Override
public synchronized void run() {
//怎么买票? 票没就结束
//循环买票
while(true){
// 票没有了,就进行停止
if(ticket <= 0){
break;
}
//有票就进行卖票
System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"票");
//买了一张,就少一张票
ticket--;
}
}
结果:
1买了第100票
1买了第99票
1买了第98票
1买了第97票
1买了第96票
1买了第95票
1买了第94票
1买了第93票
1买了第92票
1买了第91票
1买了第90票
1买了第89票
1买了第88票
1买了第87票
1买了第86票
1买了第85票
1买了第84票
1买了第83票
1买了第82票
1买了第81票
1买了第80票
1买了第79票
1买了第78票
1买了第77票
1买了第76票
1买了第75票
1买了第74票
1买了第73票
1买了第72票
1买了第71票
1买了第70票
1买了第69票
1买了第68票
1买了第67票
1买了第66票
1买了第65票
1买了第64票
1买了第63票
1买了第62票
1买了第61票
1买了第60票
1买了第59票
1买了第58票
1买了第57票
1买了第56票
1买了第55票
1买了第54票
1买了第53票
1买了第52票
1买了第51票
1买了第50票
1买了第49票
1买了第48票
1买了第47票
1买了第46票
1买了第45票
1买了第44票
1买了第43票
1买了第42票
1买了第41票
1买了第40票
1买了第39票
1买了第38票
1买了第37票
1买了第36票
1买了第35票
1买了第34票
1买了第33票
1买了第32票
1买了第31票
1买了第30票
1买了第29票
1买了第28票
1买了第27票
1买了第26票
1买了第25票
1买了第24票
1买了第23票
1买了第22票
1买了第21票
1买了第20票
1买了第19票
1买了第18票
1买了第17票
1买了第16票
1买了第15票
1买了第14票
1买了第13票
1买了第12票
1买了第11票
1买了第10票
1买了第9票
1买了第8票
1买了第7票
1买了第6票
1买了第5票
1买了第4票
1买了第3票
1买了第2票
1买了第1票
分析结果:
线程名为1 的线程,执行了全部的票 。
- 为什么是线程名为1 的线程 执行? 原因:根据 cpu 自动调度的。
- 为什么线程1 执行了全部?
原因:synchronized 标注在了run()方法上了。
代表了,3个线程,进行排队(形成队列)停留在run()方法的前面,一个一个的等待执行。cpu调度,把锁(this)随机分配给任意的一个线程。(由于线程1先new ,所以先进行调度)。run()执行完了后,ticket等于0了,其他的线程拿到了锁,进入了run()中,由于ticket等于0,所以相当于没有执行。
添加方式二:代码块(注意位置)
//在这个方法中,进行买票
@Override
public void run() {
synchronized(this){
//怎么买票? 票没就结束
//循环买票
while(true){
// 票没有了,就进行停止
if(ticket <= 0){
break;
}
//有票就进行卖票
System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"票");
//买了一张,就少一张票
ticket--;
}
}
}
结果:
1买了第100票
1买了第99票
1买了第98票
1买了第97票
1买了第96票
1买了第95票
1买了第94票
1买了第93票
1买了第92票
1买了第91票
1买了第90票
1买了第89票
1买了第88票
1买了第87票
1买了第86票
1买了第85票
1买了第84票
1买了第83票
1买了第82票
1买了第81票
1买了第80票
1买了第79票
1买了第78票
1买了第77票
1买了第76票
1买了第75票
1买了第74票
1买了第73票
1买了第72票
1买了第71票
1买了第70票
1买了第69票
1买了第68票
1买了第67票
1买了第66票
1买了第65票
1买了第64票
1买了第63票
1买了第62票
1买了第61票
1买了第60票
1买了第59票
1买了第58票
1买了第57票
1买了第56票
1买了第55票
1买了第54票
1买了第53票
1买了第52票
1买了第51票
1买了第50票
1买了第49票
1买了第48票
1买了第47票
1买了第46票
1买了第45票
1买了第44票
1买了第43票
1买了第42票
1买了第41票
1买了第40票
1买了第39票
1买了第38票
1买了第37票
1买了第36票
1买了第35票
1买了第34票
1买了第33票
1买了第32票
1买了第31票
1买了第30票
1买了第29票
1买了第28票
1买了第27票
1买了第26票
1买了第25票
1买了第24票
1买了第23票
1买了第22票
1买了第21票
1买了第20票
1买了第19票
1买了第18票
1买了第17票
1买了第16票
1买了第15票
1买了第14票
1买了第13票
1买了第12票
1买了第11票
1买了第10票
1买了第9票
1买了第8票
1买了第7票
1买了第6票
1买了第5票
1买了第4票
1买了第3票
1买了第2票
1买了第1票
结果分析:
1. 三个线程同时进入了run()方法中。
在这个方法的前面进行停下来了。进行排队(队列),一个一个的执行该方法。
当一个线程执行完后,ticket==0了,2,3线程拿到了this对象,不会执行什么了。
和前面的结果一样。
synchronized(this){
//怎么买票? 票没就结束
//循环买票
while(true){
// 票没有了,就进行停止
if(ticket <= 0){
break;
}
//有票就进行卖票
System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"票");
//买了一张,就少一张票
ticket--;
}
}
添加方式三:代码块(注意位置)
//在这个方法中,进行买票
@Override
public void run() {
//怎么买票? 票没就结束
//循环买票
while(true){
synchronized (this){
// 票没有了,就进行停止
if(ticket <= 0){
break;
}
//有票就进行卖票
System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"票");
//买了一张,就少一张票
ticket--;
}
}
}
结果:
1买了第100票
1买了第99票
1买了第98票
1买了第97票
1买了第96票
1买了第95票
1买了第94票
1买了第93票
1买了第92票
1买了第91票
1买了第90票
1买了第89票
1买了第88票
1买了第87票
1买了第86票
1买了第85票
1买了第84票
1买了第83票
1买了第82票
1买了第81票
1买了第80票
1买了第79票
1买了第78票
1买了第77票
1买了第76票
1买了第75票
1买了第74票
1买了第73票
1买了第72票
1买了第71票
1买了第70票
1买了第69票
1买了第68票
1买了第67票
1买了第66票
1买了第65票
1买了第64票
1买了第63票
1买了第62票
1买了第61票
1买了第60票
1买了第59票
1买了第58票
1买了第57票
1买了第56票
1买了第55票
1买了第54票
1买了第53票
1买了第52票
1买了第51票
1买了第50票
1买了第49票
1买了第48票
1买了第47票
1买了第46票
1买了第45票
1买了第44票
1买了第43票
1买了第42票
1买了第41票
1买了第40票
1买了第39票
1买了第38票
1买了第37票
3买了第36票
3买了第35票
3买了第34票
3买了第33票
3买了第32票
3买了第31票
3买了第30票
3买了第29票
3买了第28票
3买了第27票
3买了第26票
3买了第25票
3买了第24票
3买了第23票
3买了第22票
3买了第21票
3买了第20票
3买了第19票
3买了第18票
3买了第17票
3买了第16票
3买了第15票
3买了第14票
3买了第13票
3买了第12票
3买了第11票
3买了第10票
3买了第9票
3买了第8票
3买了第7票
3买了第6票
3买了第5票
3买了第4票
3买了第3票
3买了第2票
3买了第1票
结果分析:
while(true){
1. 代表了3个线程都进入了 while()方法,
在下面的这个方法进行排队(队列),一个一个的拿锁,进行执行。
这种情况有时,会跟情况1的结果相同,但是执行的过程是不同的,下面的是由于cpu执行的过快,导致的。
可以添加Thread.sleep()方法,便可以看见不同的结果。()如下
synchronized (this){
// 票没有了,就进行停止
if(ticket <= 0){
break;
}
//有票就进行卖票
System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"票");
//买了一张,就少一张票
ticket--;
}
}
@Override
public void run() {
//怎么买票? 票没就结束
//循环买票
while(true){
synchronized (this){
// 票没有了,就进行停止
if(ticket <= 0){
break;
}
//有票就进行卖票
System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"票");
//买了一张,就少一张票
ticket--;
}
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果:
1买了第100票
3买了第99票
2买了第98票
1买了第97票
3买了第96票
2买了第95票
3买了第94票
2买了第93票
1买了第92票
1买了第91票
2买了第90票
3买了第89票
1买了第88票
2买了第87票
3买了第86票
1买了第85票
2买了第84票
3买了第83票
1买了第82票
2买了第81票
3买了第80票
1买了第79票
2买了第78票
3买了第77票
3买了第76票
1买了第75票
2买了第74票
3买了第73票
2买了第72票
1买了第71票
2买了第70票
1买了第69票
3买了第68票
2买了第67票
1买了第66票
1买了第65票
2买了第64票
3买了第63票
2买了第62票
1买了第61票
3买了第60票
1买了第59票
2买了第58票
3买了第57票
1买了第56票
2买了第55票
3买了第54票
2买了第53票
1买了第52票
3买了第51票
2买了第50票
1买了第49票
3买了第48票
1买了第47票
2买了第46票
3买了第45票
1买了第44票
2买了第43票
1买了第42票
2买了第41票
3买了第40票
2买了第39票
3买了第38票
1买了第37票
3买了第36票
1买了第35票
2买了第34票
2买了第33票
3买了第32票
1买了第31票
3买了第30票
2买了第29票
1买了第28票
2买了第27票
3买了第26票
3买了第25票
1买了第24票
2买了第23票
1买了第22票
2买了第21票
3买了第20票
3买了第19票
2买了第18票
1买了第17票
2买了第16票
3买了第15票
1买了第14票
3买了第13票
1买了第12票
2买了第11票
1买了第10票
3买了第9票
2买了第8票
3买了第7票
1买了第6票
2买了第5票
1买了第4票
3买了第3票
1买了第2票
2买了第1票
添加方式四:代码块(注意位置)
//在这个方法中,进行买票
@Override
public void run() {
//怎么买票? 票没就结束
//循环买票
while(true){
// 票没有了,就进行停止
if(ticket <= 0){
break;
}
synchronized (this){
//有票就进行卖票
System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"票");
//买了一张,就少一张票
ticket--;
}
}
}
结果
1买了第100票
1买了第99票
1买了第98票
1买了第97票
1买了第96票
1买了第95票
1买了第94票
1买了第93票
1买了第92票
1买了第91票
1买了第90票
1买了第89票
1买了第88票
1买了第87票
1买了第86票
1买了第85票
1买了第84票
1买了第83票
1买了第82票
1买了第81票
1买了第80票
1买了第79票
1买了第78票
1买了第77票
1买了第76票
1买了第75票
1买了第74票
1买了第73票
1买了第72票
1买了第71票
1买了第70票
1买了第69票
1买了第68票
1买了第67票
1买了第66票
1买了第65票
1买了第64票
1买了第63票
1买了第62票
1买了第61票
1买了第60票
1买了第59票
1买了第58票
1买了第57票
1买了第56票
1买了第55票
1买了第54票
1买了第53票
1买了第52票
1买了第51票
1买了第50票
1买了第49票
1买了第48票
1买了第47票
1买了第46票
1买了第45票
1买了第44票
1买了第43票
1买了第42票
1买了第41票
1买了第40票
1买了第39票
1买了第38票
1买了第37票
1买了第36票
1买了第35票
1买了第34票
1买了第33票
1买了第32票
1买了第31票
1买了第30票
1买了第29票
1买了第28票
1买了第27票
1买了第26票
1买了第25票
1买了第24票
1买了第23票
1买了第22票
1买了第21票
1买了第20票
1买了第19票
1买了第18票
1买了第17票
1买了第16票
1买了第15票
1买了第14票
1买了第13票
1买了第12票
1买了第11票
1买了第10票
1买了第9票
1买了第8票
1买了第7票
1买了第6票
1买了第5票
1买了第4票
1买了第3票
1买了第2票
1买了第1票
2买了第0票
3买了第-1票
结果详解
while(true){
// 票没有了,就进行停止
if(ticket <= 0){
break;
}
可以发现,结果中有,1,0,-1 ,出问题了是吧。
分析:所以线程进入了这里,发现了同步代码块,进行排队+一个一个的获取锁,然后执行。
那么,当ticket==1时,而且,3个线程都进入这里了,因为判断ticket <=0 的语句是在上面。
直接执行即可。所以会有这种 1,0,-1的结果。
synchronized (this){
//有票就进行卖票
System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"票");
//买了一张,就少一张票
ticket--;
}
}