java线程安全问题及其解决

java线程安全问题

出现原因:多个线程之间存在“共享数据”(即共同操作的变量,“ticket”)

车站卖票案例:

定义窗口1类实现Runnable接口
class Window1 implements Runnable{
	private int ticket = 100;
	@override
	public void run(){
		while(true){
			if(ticket > 0 ){

				//为了让线程安全问题更明显(错票)
				try{
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				
				System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
				
				/*
					如果放在这里就是重票
					try{
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				*/

				ticket--;
			} else {
				break;
			}
		}
	}

定义主方法
public class WindowTest1 {
	public static void main(String[] args) {
		Window1 w = new Window1();
		
		Thread t1 = new Thread(w);
		Thread t2 = new Thread(w);
		Thread t3 = new Thread(w);

		t1.setName("窗口1");
		t2.setName("窗口2");
		t3.setName("窗口3");

		t1.start();
		t2.start();
		t3.start();
	}

运行结果如下:
在这里插入图片描述在这里插入图片描述

如何解决:

方式一:同步代码块

synchronized(同步监视器) { //需要的代码 }

说明:
1) 操作共享数据的代码,即为需要被同步的代码
2)同步监视器,俗称“锁”。任何一个类的对象,都可以充当锁
要求:多个线程之间必须共用一个锁
补充:
在实现Runnable接口创建多线程时,可以考虑使用this充当同步监视器
在继承Thread类创建多线程时,可以考虑使用当前类.class充当同步监视器

修改如下:

class Window1 implements Runnable {
		private int ticket = 100;
		Object obj = new Object();
		@override
		public void run() {
			while(true) {
				synchronized (obj) {
					if (ticket > 0 ) {
					
						try{
							Thread.sleep(100);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
				
						System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
						ticket--;
					} else {
						break;
					}
				}
			}
}

方式二:同步方法

如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明为同步的。
总结:
1.同步方法仍然涉及到同步监视器,只是我们不需要显示的声明
2.非静态的同步方法,同步监视器是:this
静态的同步方法,同步监视器是:当前类本身
代码如下;

	private static synchronized  void show(){
}

方式三:Lock锁 —JDK5.0新增

> class Window implements Runnable {
> 	private int ticket = 100;
> 	//1.创建lock对象
> 	private ReentrantLock lock = new ReentrantLock(fair:true);
> 
> 	@override
> 	public void run() {
> 		while(true) {
> 			try {
> 
> 				//2.调用锁定方法lock()
> 				lock.lock();
> 
> 				if(ticket > 0) {
		
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
		
	> 				System.out.println(Thread.currentThread().getName() + ":售票,票号为:" + ticket);
	> 				ticket--;
> 			} else {
> 				break;
> 			} finally {
> 				//3.调用解锁方法:unlock()
> 				lock.unlock();
> 		}
> 	}
> }

	public class LockTest {
		public static void main(String[] args) {
			Window w = new Window();

			Thread t1 = new Thread(w);
			Thread t2 = new Thread(w);
			Thread t3 = new Thread(w);

			t1.setName("窗口1");
			t2.setName("窗口2");
			t3.setName("窗口3");

			t1.start();
			t2.start();
			t3.start();

synchronized与lock的异同:
相同:二者可以解决线程安全问题
不同:synchronized机制在执行完相应的同步代码以后,自动释放同步监视器,而lock需要手动释放

采用同步方式:
好处:解决了线程的安全问题。
局限性:操作同步代码时,只能有一个线程参与,其他线程等待。相当于一个单线程的过程,效率低

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值