线程安全

为什么有线程安全问题?

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

案例:需求现在有100张火车票,有两个窗口同时抢火车票,请使用多线程模拟抢票效果。

class ThreadTrain1 implements Runnable {
	private int count = 100;
	private static Object oj = new Object();

	@Override
	public void run() {
		while (count > 0) {
			try {
				Thread.sleep(50);
			} catch (Exception e) {
				// TODO: handle exception
			}
			sale();
		}
	}

	public void sale() {
		// 前提 多线程进行使用、多个线程只能拿到一把锁。
		// 保证只能让一个线程 在执行 缺点效率降低
		// synchronized (oj) {
//		if (count > 0) {
			System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - count + 1) + "票");
			count--;
//		}
		// }
	}
}

public class ThreadDemo {
	public static void main(String[] args) {
		ThreadTrain1 threadTrain1 = new ThreadTrain1();
		Thread t1 = new Thread(threadTrain1, "①号窗口");
		Thread t2 = new Thread(threadTrain1, "②号窗口");
		t1.start();
		t2.start();
	}
}

运行结果:

在这里插入图片描述
一号窗口和二号窗口同时出售火车第一张和第七张,部分火车票会重复出售。
结论发现,多个线程共享同一个全局成员变量时,做写的操作可能会发生数据冲突问题。

线程安全解决办法:

问:如何解决多线程之间线程安全问题?

答:使用多线程之间同步synchronized或使用锁(lock)。

问:为什么使用线程同步或使用锁能解决线程安全问题呢?

答:将可能会发生数据冲突问题(线程不安全问题),只能让当前一个线程进行执行。代码执行完成后释放锁,让后才能让其他线程进行执行。这样的话就可以解决线程不安全问题。

问:什么是多线程之间同步?

答:当多个线程共享同一个资源,不会受到其他线程的干扰。

同步代码块

什么是同步代码块?
答:就是将可能会发生线程安全问题的代码,给包括起来。

synchronized(同一个数据){
 可能会发生线程冲突问题
}

就是同步代码块

synchronized(对象)//这个对象可以为任意对象 
{ 
    需要被同步的代码 
} 

对象如同锁,持有锁的线程可以在同步中执行
没持有锁的线程即使获取CPU的执行权,也进不去

同步的前提:
1,必须要有两个或者两个以上的线程
2,必须是多个线程使用同一个锁
必须保证同步中只能有一个线程在运行
好处:解决了多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源、抢锁的资源。

 private static Object oj = new Object();   	
    public void sale() {
    		// 前提 多线程进行使用、多个线程只能拿到一把锁。
    		// 保证只能让一个线程 在执行 缺点效率降低
    		 synchronized (oj) {
    		if (count > 0) {
    			System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - count + 1) + "票");
    			count--;
    		}
    		 }
    	}

同步函数

什么是同步函数?
答:在方法上修饰synchronized 称为同步函数

public synchronized void sale() {
			if (trainCount > 0) { 
try {
					Thread.sleep(40);
				} catch (Exception e) {
				}
				System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - trainCount + 1) + "张票.");
				trainCount--;
			}
	}

同步函数用的是什么锁?

答:同步函数使用this锁。
证明方式: 一个线程使用同步代码块(this明锁),另一个线程使用同步函数。如果两个线程抢票不能实现同步,那么会出现数据错误。

package com.itedu;

class ThreadTrain2 implements Runnable {
	private int count = 100;
	public boolean flag = true;
	private static Object oj = new Object();

	@Override
	public void run() {
		if (flag) {

			while (count > 0) {

				synchronized (this) {
					if (count > 0) {
						try {
							Thread.sleep(50);
						} catch (Exception e) {
							// TODO: handle exception
						}
						System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - count + 1) + "票");
						count--;
					}
				}

			}

		} else {
			while (count > 0) {
				sale();
			}
		}

	}

	public synchronized void sale() {
		// 前提 多线程进行使用、多个线程只能拿到一把锁。
		// 保证只能让一个线程 在执行 缺点效率降低
		// synchronized (oj) {
		if (count > 0) {
			try {
				Thread.sleep(50);
			} catch (Exception e) {
				// TODO: handle exception
			}
			System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - count + 1) + "票");
			count--;
		}
		// }
	}
}

public class ThreadDemo2 {
	public static void main(String[] args) throws InterruptedException {
		ThreadTrain2 threadTrain1 = new ThreadTrain2();
		Thread t1 = new Thread(threadTrain1, "①号窗口");
		Thread t2 = new Thread(threadTrain1, "②号窗口");
		t1.start();
		Thread.sleep(40);
		threadTrain1.flag = false;
		t2.start();
	}
}

静态同步函数

答:什么是静态同步函数?
方法上加上static关键字,使用synchronized 关键字修饰 或者使用类.class文件。
静态的同步函数使用的锁是 该函数所属字节码文件对象
可以用 getClass方法获取,也可以用当前 类名.class 表示。

synchronized (ThreadTrain.class) {
			System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - trainCount + 1) + "张票.");
			trainCount--;
			try {
				Thread.sleep(100);
			} catch (Exception e) {
			}
}

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

多线程死锁

什么是多线程死锁?
答:同步中嵌套同步,导致锁无法释放

package com.itedu;

class ThreadTrain6 implements Runnable {
	// 这是货票总票数,多个线程会同时共享资源
	private int trainCount = 100;
	public boolean flag = true;
	private Object mutex = new Object();

	@Override
	public void run() {
		if (flag) {
			while (true) {
				synchronized (mutex) {
					// 锁(同步代码块)在什么时候释放? 代码执行完, 自动释放锁.
					// 如果flag为true 先拿到 obj锁,在拿到this 锁、 才能执行。
					// 如果flag为false先拿到this,在拿到obj锁,才能执行。
					// 死锁解决办法:不要在同步中嵌套同步。
					sale();
				}
			}
		} else {
			while (true) {
				sale();
			}
		}
	}

	public synchronized void sale() {
		synchronized (mutex) {
			if (trainCount > 0) {
				try {
					Thread.sleep(40);
				} catch (Exception e) {

				}
				System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - trainCount + 1) + "张票.");
				trainCount--;
			}
		}
	}
}

public class DeadlockThread {

	public static void main(String[] args) throws InterruptedException {

		ThreadTrain6 threadTrain = new ThreadTrain6(); // 定义 一个实例
		Thread thread1 = new Thread(threadTrain, "一号窗口");
		Thread thread2 = new Thread(threadTrain, "二号窗口");
		thread1.start();
		Thread.sleep(40);
		threadTrain.flag = false;
		thread2.start();
	}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值