synchronized(this)引发的线程不安全问题

业务场景说明:

模拟多个线程同时抢购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】张火车票
 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值