一、目标
- 理解线程安全?
- synchronized用法
- 死锁
二、什么是线程安全
what?
当多个线程同时共享,同一个全局变量或静态变量,做写的操作时,可能会发生数据冲突问题,也就是线程安全问题。
但是做读操作是不会发生数据冲突问题。
需求: 现在有100张火车票,有两个窗口同时抢火车票,请使用多线程模拟抢票效果。
/**
* @ Author : 冯旭
* @ Description : 火车票线程
* @ CreateDate : 2020/1/8$ 17:08$
* @ UpdateUser : 修改人
* @ UpdateDate : 2020/1/8$ 17:08$
* @ UpdateRemark : 修改内容
* @ Version : 1.0
* 〈坚持灵活 灵活坚持〉
*/
public class TreadDemo01 implements Runnable {
private int count=100;
@Override
public void run() {
while (count>0){
try {
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - count + 1) + "张票.");
count--;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Main {
public static void main(String[] args) {
TreadDemo01 treadDemo01 = new TreadDemo01();
Thread thread01 = new Thread(treadDemo01);
Thread thread03 = new Thread(treadDemo01);
Thread thread02 = new Thread(treadDemo01);
thread01.start();
thread02.start();
thread03.start();
}
}
当多个线程同时进入循环时,循环还没有执行完,退出cpu,继续在竞争cpu的执行权。出现线程安全问题
三、 线程安全解决办法
如何解决多线程之间线程安全问题?
答:使用多线程之间同步或使用锁(lock)。
问:为什么使用线程同步或使用锁能解决线程安全问题呢?
答:将可能会发生数据冲突问题(线程不安全问题),只能让当前一个线程进行执行。代码执行完成后释放锁,让后才能让其他线程进行执行。这样的话就可以解决线程不安全问题。
问:什么是多线程之间同步?
答:当多个线程共享同一个资源,不会受到其他线程的干扰。
3.1 使用同步代码块
什么是同步代码块?
答:就是将可能会发生线程安全问题的代码,给包括起来。
/**
* @ Author : 冯旭
* @ Description : 线程火车票测试
* @ CreateDate : 2020/1/8$ 17:08$
* @ UpdateUser : 修改人
* @ UpdateDate : 2020/1/8$ 17:08$
* @ UpdateRemark : 修改内容
* @ Version : 1.0
* 〈坚持灵活 灵活坚持〉
*/
public class TreadDemo01 implements Runnable {
private int count=100;
//自定义锁(必须共享同一个锁) 实例化
private Object object = new Object();
@Override
public void run() {
while (count>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//同步代码块
synchronized (object){
if(count>0){
System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - count + 1) + "张票.");
}
}
count--;
}
}
}
四、 同步函数
什么是同步函数?
答:在方法上修饰synchronized 称为同步函数
/**
* @ Author : 冯旭
* @ Description : 线程火车票测试
* @ CreateDate : 2020/1/8$ 17:08$
* @ UpdateUser : 修改人
* @ UpdateDate : 2020/1/8$ 17:08$
* @ UpdateRemark : 修改内容
* @ Version : 1.0
* 〈坚持灵活 灵活坚持〉
*/
public class TreadDemo01 implements Runnable {
private int count=100;
//自定义锁(必须共享同一个锁) 实例化
private Object object = new Object();
@Override
public void run() {
while (count>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
sild();
//同步代码块
// synchronized (object){
// }
}
}
//同步函数
public synchronized void sild(){
if(count>0){
System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - count + 1) + "张票.");
}
count--;
}
}
同步函数用的是什么锁?
用的是this锁
证明:一个线程使用同步代码块(this明锁),另一个线程使用同步函数。如果两个线程抢票不能实现同步,那么会出现数据错误。
public class Main {
public static void main(String[] args) {
TreadDemo01 treadDemo01 = new TreadDemo01();
Thread thread01 = new Thread(treadDemo01);
Thread thread03 = new Thread(treadDemo01);
Thread thread02 = new Thread(treadDemo01);
thread01.start();
try {
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
treadDemo01.flag=false;
thread02.start();
try {
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
treadDemo01.flag=false;
thread03.start();
}
}
/**
* @ Author : 冯旭
* @ Description : 线程火车票测试
* @ CreateDate : 2020/1/8$ 17:08$
* @ UpdateUser : 修改人
* @ UpdateDate : 2020/1/8$ 17:08$
* @ UpdateRemark : 修改内容
* @ Version : 1.0
* 〈坚持灵活 灵活坚持〉
*/
public class TreadDemo01 implements Runnable {
private int count=100;
public boolean flag = true;
//自定义锁(必须共享同一个锁) 实例化
private Object object = new Object();
@Override
public void run() {
if (flag) {
while (true){
//同步代码块
synchronized (this){
if(count>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - count + 1) + "张票.");
count--;
}
}
//同步代码块
// synchronized (object){
// }
}
}else{
while (true){
sild();
}
}
}
//同步函数
public synchronized void sild(){
if(count>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - count + 1) + "张票.");
count--;
}
}
}
五、 静态同步函数
什么是静态同步函数?
方法上加上static关键字,使用synchronized 关键字修饰 或者使用类.class文件。
静态的同步函数使用的锁是 该函数所属字节码文件对象
可以用 getClass方法获取,也可以用当前 类名.class 表示。
/**
* @ Author : 冯旭
* @ Description : 线程火车票测试
* @ CreateDate : 2020/1/8$ 17:08$
* @ UpdateUser : 修改人
* @ UpdateDate : 2020/1/8$ 17:08$
* @ UpdateRemark : 修改内容
* @ Version : 1.0
* 〈坚持灵活 灵活坚持〉
*/
public class TreadDemo01 implements Runnable {
private int count=100;
public boolean flag = true;
//自定义锁(必须共享同一个锁) 实例化
private Object object = new Object();
@Override
public void run() {
// if (flag) {
while (true){
//同步代码块
synchronized (TreadDemo01.class){
if(count>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - count + 1) + "张票.");
count--;
}
}
//同步代码块
// synchronized (object){
// }
}
// }else{
// while (true){
// sild();
// }
// }
}
}
synchronized 修饰方法使用锁是当前this锁。
synchronized 修饰静态方法使用锁是当前类的字节码文件
六、 多线程死锁
什么是多线程死锁?
同步中嵌套同步,导致锁无法释放
/**
* @ Author : 冯旭
* @ Description : 线程火车票测试
* @ CreateDate : 2020/1/8$ 17:08$
* @ UpdateUser : 修改人
* @ UpdateDate : 2020/1/8$ 17:08$
* @ UpdateRemark : 修改内容
* @ Version : 1.0
* 〈坚持灵活 灵活坚持〉
*/
public class TreadDemo01 implements Runnable {
private int count=100;
public boolean flag = true;
//自定义锁(必须共享同一个锁) 实例化
private Object object = new Object();
@Override
public void run() {
if (flag) {
while (true){
//同步代码块
// 锁(同步代码块)在什么时候释放? 代码执行完, 自动释放锁.
// 如果flag为true 先拿到 obj锁,在拿到this 锁、 才能执行。
// 如果flag为false先拿到this,在拿到obj锁,才能执行。
// 死锁解决办法:不要在同步中嵌套同步。
synchronized (object){
sild();
}
}
}else{
while (true){
sild();
}
}
}
//同步函数
public synchronized void sild(){
synchronized (object){
if(count>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - count + 1) + "张票.");
count--;
}
}
}
}
七、 面试题
-
问:什么是多线程安全?
答:当多个线程同时共享,同一个全局变量或静态变量,做写的操作时,可能会发生数据冲突问题,也就是线程安全问题。做读操作是不会发生数据冲突问题。 -
问:如何解决多线程之间线程安全问题?
答:使用多线程之间同步或使用锁(lock)。 -
问:为什么使用线程同步或使用锁能解决线程安全问题呢?
答:将可能会发生数据冲突问题(线程不安全问题),只能让当前一个线程进行执行。被包裹的代码执行完成后释放锁,让后才能让其他线程进行执行。这样的话就可以解决线程不安全问题. -
问:什么是多线程之间同步?
答:当多个线程共享同一个资源,不会受到其他线程的干扰。 -
问:什么是同步代码块?
答:就是将可能会发生线程安全问题的代码,给包括起来。只能让当前一个线程进行执行,被包裹的代码执行完成之后才能释放所,让后才能让其他线程进行执行。 -
问:多线程同步的分类?
a. 使用同步代码块。synchronized(同一个数据){
可能会发生线程冲突问题
}b. 使用同步函数
在方法上修饰synchronized 称为同步函数。c. 静态同步函数
方法上加上static关键字,使用synchronized 关键字修饰 为静态同步函数
静态的同步函数使用的锁是 该函数所属字节码文件对象 -
问:同步代码块与同步函数区别?
答: 同步代码使用自定锁(明锁) 同步函数使用this锁 -
问:同步函数与静态同步函数区别? (例如现在一个静态方法和一个非静态静态怎么实现同步?)
同步函数使用this锁 静态同步函数使用字节码文件,也就是类.class -
问:什么是多线程死锁?
同步中嵌套同步
解决办法:同步中尽量不要嵌套同步