一、什么是线程安全问题
为什么有线程安全问题?
当多个线程同时共享同一个全局变量或静态变量,做写的操作(修改变量值)时,可能会发生数据冲突问题,也就是线程安全问题。但是做读操作时不会发生数据冲突问题。
案例:需求现在有100张火车票,有两个窗口同时抢火车票,请使用多线程模拟抢票效果。
/**
* 需求现在有100张火车票,有两个窗口同时抢火车票,请使用多线程模拟抢票效果。
* Created by yz on 2018/04/01.
*/
public class ThreadDemo {
public static void main(String[] args) {
// t1 t2同时共享同一变量trainCount
ThreadTrain threadTrain = new ThreadTrain();
Thread t1 = new Thread(threadTrain, "窗口1");
Thread t2 = new Thread(threadTrain, "窗口2");
t1.start();
t2.start();
}
}
// 售票窗口
class ThreadTrain implements Runnable{
// 总共有100张火车票
private int trainCount = 100;
public void run() {
while (trainCount > 0){
try {
// 休眠50秒
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 出售火车票
sale();
}
}
// 卖票方法
public void sale(){
System.out.println(Thread.currentThread().getName()+",出售第"+(100-trainCount+1)+"张票");
trainCount--;
}
}
运行结果:
原因解析:
卖票方法加判断,不能百分百解决问题
// 卖票方法
public void sale(){
if(trainCount > 0){
System.out.println(Thread.currentThread().getName()+",出售第"+(100-trainCount+1)+"张票");
trainCount--;
}
}
原因解析:
多个线程共享同一个局部变量,会发生线程安全问题吗?不会
线程安全问题,有哪些解决办法?
解决办法:
synchronized --- 自动锁
lock --- jdk1.5并发包 --- 手动锁
线程之间如何同步?
同步是保证数据原子性,原子性就是数据不能受到其他线程干扰。
二、使用同步代码块解决线程安全问题
什么地方需要考虑加锁?
考虑在真正产生共享同一个全局变量的时候使用,不要用synchronized去包裹整个代码。
// 卖票方法
public void sale(){
// 同步代码块 synchronized 包裹需要线程安全的问题。
synchronized (object){
if(trainCount > 0){
System.out.println(Thread.currentThread().getName()+",出售第"+(100-trainCount+1)+"张票");
trainCount--;
}
}
}
什么是同步代码块?
答:就是将可能会发生线程安全问题的代码,给包裹起来
synchronized(对象){ // 这个对象可以为任意对象
可能会发生线程冲突的问题
}
对象如同锁,持有锁的线程可以在同步块中执行,没持有锁的线程,即使获取cup的执行权,也进不去。
使用synchronized必须有一些条件:
1.必须要有两个或者两个以上的线程需要发生同步。
2.多个线程想同步,必须使用同一把锁
3.保证只有一个线程进行执行
synchronized原理:
1.首先有一个线程已经拿到了锁,其他线程已经有cup执行权,一直排队,等待释放锁。
2.锁是在什么时候释放?代码执行完毕或者程序抛出异常都会被释放掉。
3.锁已经被释放掉的话,其他线程开始进行抢锁(资源竞争),谁抢到谁进入同步中去,其他线程继续等待。
好处:解决了多线程的安全问题
弊端:效率非常低,多个线程需要判断锁,比较消耗资源,抢锁的资源。
什么是线程之间同步?
同步是保证多个线程之间共享同一个全局变量数据安全问题,保证数据原子性。
synchronized 只能有一个线程进行执行(就像厕所只有一个坑,好多人等着上厕所,谁进入厕所谁上锁,其他人在外等待),这个线程不释放锁的话,其他线程就一直等,就会产生死锁问题。
三、同步函数使用、this锁
同步函数
什么是同步函数?
答:在方法上加上synchronized进行修饰 称为同步函数。
// 卖票方法
public synchronized void sale(){
if(trainCount > 0){
System.out.println(Thread.currentThread().getName()+",出售第"+(100-trainCount+1)+"张票");
trainCount--;
}
}
同步函数使用的是什么锁?this锁
怎么证明同步函数使用的是this锁?
两个线程之间实现同步,一个线程使用this锁同步代码块,一个线程使用同步函数,这两个线程如果同步,说明同步函数使用的是this锁。
/**
* 需求现在有100张火车票,有两个窗口同时抢火车票,请使用多线程模拟抢票效果。
* Created by yz on 2018/04/01.
*/
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
// t1 t2同时共享同一变量trainCount
ThreadTrain threadTrain = new ThreadTrain()