业务场景说明:
模拟多个线程同时抢购100张火车票;
代码展示:
/**
* 对synchronized(this)的一些理解:
* 一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
* 二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
* 三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
* 四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
* 五、synchronized同步方法与synchronized(this)同步代码块使用的是同一把锁.
*/
public class MutilThreadDemo_02 {
public static void main(String[] args) throws InterruptedException {
/* 1、两个线程、两个实体,无法验证 */
// TrainTicket2 tt1 = new TrainTicket2();
// TrainTicket2 tt2 = new TrainTicket2();
// new Thread(tt1, "窗口①").start();
// new Thread(tt2, "窗口二").start();
/* 未发生线程安全问题 */
/* 2、两个线程、同一个实体、调用相同的方法,无法验证 */
// TrainTicket2 tt = new TrainTicket2();
// new Thread(tt, "窗口①").start();
// new Thread(tt, "窗口二").start();
/* 出现线程安全问题 :窗口二<true>抢到了第【101】张火车票.why? */
/* 3、两个线程、同一个实体、调用不同的方法,验证synchronized使用this锁 */
TrainTicket2 tt = new TrainTicket2();
new Thread(tt, "窗口①").start();
Thread.sleep(40);
tt.flag = false;
new Thread(tt, "窗口二").start();
/* 出现线程安全问题 :相同的实体,同一把锁,为什么出现了第【101】张火车票? */
}
}
class TrainTicket2 implements Runnable {
private int ticketCount = 100; // 初始化100张火车票(全局变量)
static Object mutex = new Object(); // 多个线程使用同一把锁mutex
public boolean flag = true;
@Override
public void run() {
if (flag) {
// synchronized (this) { // synchronized放这里只有一个线程在执行,why?
while (ticketCount > 0) {
try {
Thread.sleep(40);
} catch (InterruptedException e) {
}
synchronized (this) { // synchronized放这里出现第101张票,发生线程不安全问题,why?
System.out.println(
Thread.currentThread().getName() + "<true>抢到了第【" + (100 - ticketCount + 1) + "】张火车票");
ticketCount--;
}
}
} else {
shopTicket();
}
}
private synchronized void shopTicket() {
while (ticketCount > 0) {
try {
Thread.sleep(40);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + ":<false>抢到了第【" + (100 - ticketCount + 1) + "】张火车票");
ticketCount--;
}
}
}
问题展示:
场景一:synchronized (this)包裹整个代码块,只有一个线程运行
代码如下:
@Override
public void run() {
if (flag) {
synchronized (this) { // synchronized放这里只有一个线程在执行,why?
while (ticketCount > 0) {
try {
Thread.sleep(40);
} catch (InterruptedException e) {
}
// synchronized (this) { // synchronized放这里出现第101张票,发生线程不安全问题,why?
System.out.println(
Thread.currentThread().getName() + "<true>抢到了第【" + (100 - ticketCount + 1) + "】张火车票");
ticketCount--;
}
}
} else {
shopTicket();
}
}
结果展示:
窗口①<true>抢到了第【1】张火车票
窗口①<true>抢到了第【2】张火车票
窗口①<true>抢到了第【3】张火车票
窗口①<true>抢到了第【4】张火车票
窗口①<true>抢到了第【5】张火车票
窗口①<true>抢到了第【6】张火车票
窗口①<true>抢到了第【7】张火车票
窗口①<true>抢到了第【8】张火车票
窗口①<true>抢到了第【9】张火车票
窗口①<true>抢到了第【10】张火车票
窗口①<true>抢到了第【11】张火车票
窗口①<true>抢到了第【12】张火车票
窗口①<true>抢到了第【13】张火车票
窗口①<true>抢到了第【14】张火车票
窗口①<true>抢到了第【15】张火车票
窗口①<true>抢到了第【16】张火车票
窗口①<true>抢到了第【17】张火车票
窗口①<true>抢到了第【18】张火车票
窗口①<true>抢到了第【19】张火车票
窗口①<true>抢到了第【20】张火车票
窗口①<true>抢到了第【21】张火车票
窗口①<true>抢到了第【22】张火车票
窗口①<true>抢到了第【23】张火车票
窗口①<true>抢到了第【24】张火车票
窗口①<true>抢到了第【25】张火车票
窗口①<true>抢到了第【26】张火车票
窗口①<true>抢到了第【27】张火车票
窗口①<true>抢到了第【28】张火车票
窗口①<true>抢到了第【29】张火车票
窗口①<true>抢到了第【30】张火车票
窗口①<true>抢到了第【31】张火车票
窗口①<true>抢到了第【32】张火车票
窗口①<true>抢到了第【33】张火车票
窗口①<true>抢到了第【34】张火车票
窗口①<true>抢到了第【35】张火车票
窗口①<true>抢到了第【36】张火车票
窗口①<true>抢到了第【37】张火车票
窗口①<true>抢到了第【38】张火车票
窗口①<true>抢到了第【39】张火车票
窗口①<true>抢到了第【40】张火车票
窗口①<true>抢到了第【41】张火车票
窗口①<true>抢到了第【42】张火车票
窗口①<true>抢到了第【43】张火车票
窗口①<true>抢到了第【44】张火车票
窗口①<true>抢到了第【45】张火车票
窗口①<true>抢到了第【46】张火车票
窗口①<true>抢到了第【47】张火车票
窗口①<true>抢到了第【48】张火车票
窗口①<true>抢到了第【49】张火车票
窗口①<true>抢到了第【50】张火车票
窗口①<true>抢到了第【51】张火车票
窗口①<true>抢到了第【52】张火车票
窗口①<true>抢到了第【53】张火车票
窗口①<true>抢到了第【54】张火车票
窗口①<true>抢到了第【55】张火车票
窗口①<true>抢到了第【56】张火车票
窗口①<true>抢到了第【57】张火车票
窗口①<true>抢到了第【58】张火车票
窗口①<true>抢到了第【59】张火车票
窗口①<true>抢到了第【60】张火车票
窗口①<true>抢到了第【61】张火车票
窗口①<true>抢到了第【62】张火车票
窗口①<true>抢到了第【63】张火车票
窗口①<true>抢到了第【64】张火车票
窗口①<true>抢到了第【65】张火车票
窗口①<true>抢到了第【66】张火车票
窗口①<true>抢到了第【67】张火车票
窗口①<true>抢到了第【68】张火车票
窗口①<true>抢到了第【69】张火车票
窗口①<true>抢到了第【70】张火车票
窗口①<true>抢到了第【71】张火车票
窗口①<true>抢到了第【72】张火车票
窗口①<true>抢到了第【73】张火车票
窗口①<true>抢到了第【74】张火车票
窗口①<true>抢到了第【75】张火车票
窗口①<true>抢到了第【76】张火车票
窗口①<true>抢到了第【77】张火车票
窗口①<true>抢到了第【78】张火车票
窗口①<true>抢到了第【79】张火车票
窗口①<true>抢到了第【80】张火车票
窗口①<true>抢到了第【81】张火车票
窗口①<true>抢到了第【82】张火车票
窗口①<true>抢到了第【83】张火车票
窗口①<true>抢到了第【84】张火车票
窗口①<true>抢到了第【85】张火车票
窗口①<true>抢到了第【86】张火车票
窗口①<true>抢到了第【87】张火车票
窗口①<true>抢到了第【88】张火车票
窗口①<true>抢到了第【89】张火车票
窗口①<true>抢到了第【90】张火车票
窗口①<true>抢到了第【91】张火车票
窗口①<true>抢到了第【92】张火车票
窗口①<true>抢到了第【93】张火车票
窗口①<true>抢到了第【94】张火车票
窗口①<true>抢到了第【95】张火车票
窗口①<true>抢到了第【96】张火车票
窗口①<true>抢到了第【97】张火车票
窗口①<true>抢到了第【98】张火车票
窗口①<true>抢到了第【99】张火车票
窗口①<true>抢到了第【100】张火车票
场景二:synchronized (this)只锁定"写"代码块,出现了线程不安全问题
代码如下:
@Override
public void run() {
if (flag) {
// synchronized (this) { // synchronized放这里只有一个线程在执行,why?
while (ticketCount > 0) {
try {
Thread.sleep(40);
} catch (InterruptedException e) {
}
synchronized (this) { // synchronized放这里出现第101张票,发生线程不安全问题,why?
System.out.println(
Thread.currentThread().getName() + "<true>抢到了第【" + (100 - ticketCount + 1) + "】张火车票");
ticketCount--;
}
}
} else {
shopTicket();
}
}
结果展示:
窗口①<true>抢到了第【1】张火车票
窗口二:<false>抢到了第【2】张火车票
窗口二:<false>抢到了第【3】张火车票
窗口二:<false>抢到了第【4】张火车票
窗口二:<false>抢到了第【5】张火车票
窗口二:<false>抢到了第【6】张火车票
窗口二:<false>抢到了第【7】张火车票
窗口二:<false>抢到了第【8】张火车票
窗口二:<false>抢到了第【9】张火车票
窗口二:<false>抢到了第【10】张火车票
窗口二:<false>抢到了第【11】张火车票
窗口二:<false>抢到了第【12】张火车票
窗口二:<false>抢到了第【13】张火车票
窗口二:<false>抢到了第【14】张火车票
窗口二:<false>抢到了第【15】张火车票
窗口二:<false>抢到了第【16】张火车票
窗口二:<false>抢到了第【17】张火车票
窗口二:<false>抢到了第【18】张火车票
窗口二:<false>抢到了第【19】张火车票
窗口二:<false>抢到了第【20】张火车票
窗口二:<false>抢到了第【21】张火车票
窗口二:<false>抢到了第【22】张火车票
窗口二:<false>抢到了第【23】张火车票
窗口二:<false>抢到了第【24】张火车票
窗口二:<false>抢到了第【25】张火车票
窗口二:<false>抢到了第【26】张火车票
窗口二:<false>抢到了第【27】张火车票
窗口二:<false>抢到了第【28】张火车票
窗口二:<false>抢到了第【29】张火车票
窗口二:<false>抢到了第【30】张火车票
窗口二:<false>抢到了第【31】张火车票
窗口二:<false>抢到了第【32】张火车票
窗口二:<false>抢到了第【33】张火车票
窗口二:<false>抢到了第【34】张火车票
窗口二:<false>抢到了第【35】张火车票
窗口二:<false>抢到了第【36】张火车票
窗口二:<false>抢到了第【37】张火车票
窗口二:<false>抢到了第【38】张火车票
窗口二:<false>抢到了第【39】张火车票
窗口二:<false>抢到了第【40】张火车票
窗口二:<false>抢到了第【41】张火车票
窗口二:<false>抢到了第【42】张火车票
窗口二:<false>抢到了第【43】张火车票
窗口二:<false>抢到了第【44】张火车票
窗口二:<false>抢到了第【45】张火车票
窗口二:<false>抢到了第【46】张火车票
窗口二:<false>抢到了第【47】张火车票
窗口二:<false>抢到了第【48】张火车票
窗口二:<false>抢到了第【49】张火车票
窗口二:<false>抢到了第【50】张火车票
窗口二:<false>抢到了第【51】张火车票
窗口二:<false>抢到了第【52】张火车票
窗口二:<false>抢到了第【53】张火车票
窗口二:<false>抢到了第【54】张火车票
窗口二:<false>抢到了第【55】张火车票
窗口二:<false>抢到了第【56】张火车票
窗口二:<false>抢到了第【57】张火车票
窗口二:<false>抢到了第【58】张火车票
窗口二:<false>抢到了第【59】张火车票
窗口二:<false>抢到了第【60】张火车票
窗口二:<false>抢到了第【61】张火车票
窗口二:<false>抢到了第【62】张火车票
窗口二:<false>抢到了第【63】张火车票
窗口二:<false>抢到了第【64】张火车票
窗口二:<false>抢到了第【65】张火车票
窗口二:<false>抢到了第【66】张火车票
窗口二:<false>抢到了第【67】张火车票
窗口二:<false>抢到了第【68】张火车票
窗口二:<false>抢到了第【69】张火车票
窗口二:<false>抢到了第【70】张火车票
窗口二:<false>抢到了第【71】张火车票
窗口二:<false>抢到了第【72】张火车票
窗口二:<false>抢到了第【73】张火车票
窗口二:<false>抢到了第【74】张火车票
窗口二:<false>抢到了第【75】张火车票
窗口二:<false>抢到了第【76】张火车票
窗口二:<false>抢到了第【77】张火车票
窗口二:<false>抢到了第【78】张火车票
窗口二:<false>抢到了第【79】张火车票
窗口二:<false>抢到了第【80】张火车票
窗口二:<false>抢到了第【81】张火车票
窗口二:<false>抢到了第【82】张火车票
窗口二:<false>抢到了第【83】张火车票
窗口二:<false>抢到了第【84】张火车票
窗口二:<false>抢到了第【85】张火车票
窗口二:<false>抢到了第【86】张火车票
窗口二:<false>抢到了第【87】张火车票
窗口二:<false>抢到了第【88】张火车票
窗口二:<false>抢到了第【89】张火车票
窗口二:<false>抢到了第【90】张火车票
窗口二:<false>抢到了第【91】张火车票
窗口二:<false>抢到了第【92】张火车票
窗口二:<false>抢到了第【93】张火车票
窗口二:<false>抢到了第【94】张火车票
窗口二:<false>抢到了第【95】张火车票
窗口二:<false>抢到了第【96】张火车票
窗口二:<false>抢到了第【97】张火车票
窗口二:<false>抢到了第【98】张火车票
窗口二:<false>抢到了第【99】张火车票
窗口二:<false>抢到了第【100】张火车票
窗口①<true>抢到了第【101】张火车票
本人百思不得其解,请大佬们留言,答疑解惑,谢谢!
改进方案:添加while (true) {...}
class TrainTicket2 implements Runnable {
private int ticketCount = 100; // 初始化100张火车票(全局变量)
public boolean flag = true;
@Override
public void run() {
if (flag) {
while (true) {
synchronized (this) {
if (ticketCount > 0) {
try {
Thread.sleep(40);
} catch (InterruptedException e) {
}
System.out.println(
Thread.currentThread().getName() + "<true>抢到了第【" + (100 - ticketCount + 1) + "】张火车票");
ticketCount--;
}
}
}
} else {
while (true) {
shopTicket();
}
}
}
private synchronized void shopTicket() {
if (ticketCount > 0) {
try {
Thread.sleep(40);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + ":<false>抢到了第【" + (100 - ticketCount + 1) + "】张火车票");
ticketCount--;
}
}
}
结果展示:
窗口①<true>抢到了第【1】张火车票
窗口①<true>抢到了第【2】张火车票
窗口①<true>抢到了第【3】张火车票
窗口①<true>抢到了第【4】张火车票
窗口①<true>抢到了第【5】张火车票
窗口①<true>抢到了第【6】张火车票
窗口①<true>抢到了第【7】张火车票
窗口①<true>抢到了第【8】张火车票
窗口①<true>抢到了第【9】张火车票
窗口①<true>抢到了第【10】张火车票
窗口①<true>抢到了第【11】张火车票
窗口①<true>抢到了第【12】张火车票
窗口①<true>抢到了第【13】张火车票
窗口①<true>抢到了第【14】张火车票
窗口①<true>抢到了第【15】张火车票
窗口①<true>抢到了第【16】张火车票
窗口①<true>抢到了第【17】张火车票
窗口二:<false>抢到了第【18】张火车票
窗口①<true>抢到了第【19】张火车票
窗口①<true>抢到了第【20】张火车票
窗口①<true>抢到了第【21】张火车票
窗口①<true>抢到了第【22】张火车票
窗口①<true>抢到了第【23】张火车票
窗口二:<false>抢到了第【24】张火车票
窗口二:<false>抢到了第【25】张火车票
窗口二:<false>抢到了第【26】张火车票
窗口①<true>抢到了第【27】张火车票
窗口二:<false>抢到了第【28】张火车票
窗口二:<false>抢到了第【29】张火车票
窗口二:<false>抢到了第【30】张火车票
窗口①<true>抢到了第【31】张火车票
窗口①<true>抢到了第【32】张火车票
窗口二:<false>抢到了第【33】张火车票
窗口二:<false>抢到了第【34】张火车票
窗口①<true>抢到了第【35】张火车票
窗口①<true>抢到了第【36】张火车票
窗口①<true>抢到了第【37】张火车票
窗口①<true>抢到了第【38】张火车票
窗口①<true>抢到了第【39】张火车票
窗口①<true>抢到了第【40】张火车票
窗口①<true>抢到了第【41】张火车票
窗口①<true>抢到了第【42】张火车票
窗口①<true>抢到了第【43】张火车票
窗口①<true>抢到了第【44】张火车票
窗口二:<false>抢到了第【45】张火车票
窗口二:<false>抢到了第【46】张火车票
窗口①<true>抢到了第【47】张火车票
窗口二:<false>抢到了第【48】张火车票
窗口二:<false>抢到了第【49】张火车票
窗口①<true>抢到了第【50】张火车票
窗口①<true>抢到了第【51】张火车票
窗口二:<false>抢到了第【52】张火车票
窗口二:<false>抢到了第【53】张火车票
窗口二:<false>抢到了第【54】张火车票
窗口二:<false>抢到了第【55】张火车票
窗口二:<false>抢到了第【56】张火车票
窗口二:<false>抢到了第【57】张火车票
窗口二:<false>抢到了第【58】张火车票
窗口二:<false>抢到了第【59】张火车票
窗口二:<false>抢到了第【60】张火车票
窗口二:<false>抢到了第【61】张火车票
窗口①<true>抢到了第【62】张火车票
窗口二:<false>抢到了第【63】张火车票
窗口二:<false>抢到了第【64】张火车票
窗口二:<false>抢到了第【65】张火车票
窗口二:<false>抢到了第【66】张火车票
窗口二:<false>抢到了第【67】张火车票
窗口①<true>抢到了第【68】张火车票
窗口①<true>抢到了第【69】张火车票
窗口①<true>抢到了第【70】张火车票
窗口二:<false>抢到了第【71】张火车票
窗口二:<false>抢到了第【72】张火车票
窗口①<true>抢到了第【73】张火车票
窗口①<true>抢到了第【74】张火车票
窗口二:<false>抢到了第【75】张火车票
窗口①<true>抢到了第【76】张火车票
窗口①<true>抢到了第【77】张火车票
窗口二:<false>抢到了第【78】张火车票
窗口二:<false>抢到了第【79】张火车票
窗口①<true>抢到了第【80】张火车票
窗口二:<false>抢到了第【81】张火车票
窗口二:<false>抢到了第【82】张火车票
窗口①<true>抢到了第【83】张火车票
窗口①<true>抢到了第【84】张火车票
窗口二:<false>抢到了第【85】张火车票
窗口二:<false>抢到了第【86】张火车票
窗口二:<false>抢到了第【87】张火车票
窗口二:<false>抢到了第【88】张火车票
窗口二:<false>抢到了第【89】张火车票
窗口①<true>抢到了第【90】张火车票
窗口二:<false>抢到了第【91】张火车票
窗口二:<false>抢到了第【92】张火车票
窗口①<true>抢到了第【93】张火车票
窗口二:<false>抢到了第【94】张火车票
窗口①<true>抢到了第【95】张火车票
窗口①<true>抢到了第【96】张火车票
窗口二:<false>抢到了第【97】张火车票
窗口①<true>抢到了第【98】张火车票
窗口①<true>抢到了第【99】张火车票
窗口①<true>抢到了第【100】张火车票