【Java多线程】多线程的线程安全及同步(synchronized)用法

Q:什么是线程安全问题?

A:当多个线程同时共享同一个全局变量或静态变量,改变变量的数据时,可能会发生数据冲突问题,也就是线程安全问题。读取变量不会发生数据冲突。

Q:当有线程安全问题时,应该怎样处理?

A:把对全局变量或静态变量做修改的代码放入同步代码块,即synchronized(){}。

PS:synchronized 修饰方法使用锁是当前this锁。synchronized 修饰静态方法使用锁是当前类的字节码文件。


案例:售票系统,假设有两个窗口同时售票,使用多线程模拟售票。

SellTicket.java
public class SellTicket implements Runnable{
	// 100张票
	private int ticketCount = 100;
	
	// 使用线程模拟售票窗口
	@Override
	public void run() {
		// 死循环,窗口不停卖票
		while (true) {
			// 未在操作变量的代码加锁(同步)
			
				// 如果票数为零,则跳出循环,结束线程(关闭窗口,停止售票)
				if (ticketCount > 0) {
//					try {
//						Thread.sleep(0);
//					} catch (Exception e) {
//						e.printStackTrace();
//					}
					System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - ticketCount + 1) + "张票.");
					ticketCount--;
				} else {
					break;
				}

		}
	}
	
	public static void main(String[] args) {
		// 单例,只有一个ticketCount
		SellTicket st = new SellTicket();
		// 创建两个线程并运行
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(st);
		t1.start();
		t2.start();
	}
}

运行结果片段
Thread-0,出售 第1张票.
Thread-0,出售 第2张票.
Thread-0,出售 第3张票.
Thread-0,出售 第4张票.
Thread-0,出售 第5张票.
Thread-1,出售 第1张票.
Thread-1,出售 第7张票.
Thread-1,出售 第8张票.
Thread-1,出售 第9张票.
Thread-1,出售 第98张票.
Thread-1,出售 第99张票.
Thread-1,出售 第100张票.
Thread-0,出售 第40张票.
可以看到线程0已经卖出了第1张票,可是线程1也卖出了第1张票,而最后线程1已经卖出了第100张票,线程0又卖出了第40张票。

让线程每卖出一张票睡眠100毫秒
				// 如果票数为零,则跳出循环,结束线程(关闭窗口,停止售票)
				if (ticketCount > 0) {
					try {
						Thread.sleep(100);
					} catch (Exception e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - ticketCount + 1) + "张票.");
					ticketCount--;
				} else {
					break;
				}

运行结果片段
Thread-1,出售 第99张票.
Thread-0,出售 第100张票.
Thread-1,出售 第101张票.
这次除了出现以上的情况,还卖出了第101张票。
这就是两个线程在同时操作一个变量,造成的线程安全问题。

怎么让线程安全?使用同步代码块(加锁)

SellTicket.java
public class SellTicket implements Runnable{
	// 100张票
	private int ticketCount = 100;
	// 同步代码块的锁
	private Object obj = new Object();
	
	// 使用线程模拟售票窗口
	@Override
	public void run() {
		// 死循环,窗口不停卖票
		while (true) {
			// 在操作变量的代码加obj锁(同步),当某个线程开始运行里面代码时,会得到锁。
			// 其他线程如果想运行同样代码,需等得到锁的代码运行完毕解锁后,再得到锁执行。
			synchronized(obj) {
				// 如果票数为零,则跳出循环,结束线程(关闭窗口,停止售票)
				if (ticketCount > 0) {
					try {
						Thread.sleep(100);
					} catch (Exception e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - ticketCount + 1) + "张票.");
					ticketCount--;
				} else {
					break;
				}
			}
		}
	}
	
	public static void main(String[] args) {
		// 单例,只有一个ticketCount
		SellTicket st = new SellTicket();
		// 创建两个线程并运行
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(st);
		t1.start();
		t2.start();
	}
}

运行结果片段
Thread-0,出售 第92张票.
Thread-0,出售 第93张票.
Thread-0,出售 第94张票.
Thread-0,出售 第95张票.
Thread-0,出售 第96张票.
Thread-0,出售 第97张票.
Thread-1,出售 第98张票.
Thread-1,出售 第99张票.
Thread-1,出售 第100张票.
同步后线程安全问题解决了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值