Java小白入门 —— 多线程之间实现同步(二)
一. 什么是线程安全:
为什么会存在线程安全问题:
当多个线程同时共享同一全局变量或静态变量时,在做读操作时不会发送数据冲突,而在做写操作时可能会发送数据冲突问题,就会出现线程安全问题。
二. 案例:
背景: 某工厂需拖运100T货物,找了3家托运公司,用多线程模拟托运情况。
代码如下:
/**
* @author MuXin
* @date 2020/10/21 16:11
*
* 某工厂需拖运100T货物,找了3家托运公司,用多线程模拟托运情况。
*/
public class ThreadDemo {
public static void main(String[] args) {
ThreadGoods threadGoods = new ThreadGoods();
Thread threadA = new Thread(threadGoods, "A公司");
Thread threadB = new Thread(threadGoods, "B公司");
Thread threadC = new Thread(threadGoods, "C公司");
threadA.start();
threadB.start();
threadC.start();
}
static class ThreadGoods implements Runnable {
//货物总量
private int goods = 100;
@Override
public void run() {
while (goods > 0) {
try {
//等待0.1s
Thread.sleep(100);
} catch (Exception e) {
}
//执行运货操作
System.out.println(Thread.currentThread().getName()+",搬运第"+(101-goods)+"T货物");
goods--;
}
}
}
}
输出结果:
当多个线程共享同一个全局成员变量时,做写的操作可能会发生数据冲突问题。
三. 线程安全解决方案:
1. 使用同步代码块
将可能出现问题的代码块包裹起来。
/**
* @author MuXin
* @date 2020/10/21 16:11
* <p>
* 某工厂需拖运100T货物,找了3家托运公司,用多线程模拟托运情况。
*/
public class ThreadDemo {
public static void main(String[] args) {
ThreadGoods threadGoods = new ThreadGoods();
Thread threadA = new Thread(threadGoods, "A公司");
Thread threadB = new Thread(threadGoods, "B公司");
Thread threadC = new Thread(threadGoods, "C公司");
threadA.start();
threadB.start();
threadC.start();
}
static class ThreadGoods implements Runnable {
//自定义多线程同步锁
private Object mutex = new Object();
//货物总量
private int goods = 100;
@Override
public void run() {
while (goods > 0) {
try {
//等待0.1s
Thread.sleep(100);
} catch (Exception e) {
}
synchronized (mutex) {
if(goods > 0) {
try {
//等待0.1s
Thread.sleep(100);
} catch (Exception e) {
}
//执行运货操作
System.out.println(Thread.currentThread().getName() + ",搬运第" + (101 - goods) + "T货物");
goods--;
}
}
}
}
}
}
运行结果:
2. 使用同步函数(synchronized锁)
在方法上修饰synchronized称为同步函数;
synchronized使用的锁是this锁。
/**
* @author MuXin
* @date 2020/10/21 16:11
* <p>
* 某工厂需拖运100T货物,找了3家托运公司,用多线程模拟托运情况。
*/
public class ThreadGoods implements Runnable {
//货物总量
private int goods = 100;
//自定义多线程同步锁
private Object mutex = new Object();
@Override
public void run() {
while (goods > 0) {
try {
//等待0.1s
Thread.sleep(100);
} catch (Exception e) {
}
moveGoods();
}
}
public synchronized void moveGoods() {
if (goods > 0) {
try {
//等待0.1s
Thread.sleep(100);
} catch (Exception e) {
}
//执行运货操作
System.out.println(Thread.currentThread().getName() + ",搬运第" + (101 - goods) + "T货物");
goods--;
}
}
}
执行结果:
3. 静态同步函数
在方法上加static关键字,使用synchronized关键词修饰,或者使用类.class文件;
静态同步函数使用的锁是该函数所属字节码文件对象。
/**
* @author MuXin
* @date 2020/10/21 16:11
* <p>
* 某工厂需拖运100T货物,找了3家托运公司,用多线程模拟托运情况。
*/
public class ThreadGoods implements Runnable {
//货物总量
private int goods = 100;
//自定义多线程同步锁
private Object mutex = new Object();
@Override
public void run() {
while (goods > 0) {
try {
//等待0.1s
Thread.sleep(100);
} catch (Exception e) {
}
moveGoods();
}
}
public void moveGoods() {
synchronized (ThreadGoods.class) {
if (goods > 0) {
try {
//等待0.1s
Thread.sleep(100);
} catch (Exception e) {
}
//执行运货操作
System.out.println(Thread.currentThread().getName() + ",搬运第" + (101 - goods) + "T货物");
goods--;
}
}
}
}
执行结果:
四. 多线程死锁:
1. 什么是多线程死锁:
在同步中嵌套同步,俗称套娃,导致锁无法释放。
2. 如何避免死锁:
1. 加锁顺序(线程按照一定的顺序加锁)
2. 加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
3. 死锁检测
五. 常见面试题:
1. 什么是线程安全
当多个线程同时共享,同一个全局变量或静态变量,做写的操作时,可能会发生数据冲突问题,也就是线程安全问题。
2. 如何解决多线程之间的线程安全问题?
1. 使用同步代码块;
2. 使用 synchronized 锁;
3. 使用静态同步函数。
3. 为什么使用线程同步或使用锁之后能解决线程安全问题?
当会发生数据冲突问题时,使得同一时间只能执行一个线程,其他线程处于阻塞状态,在执行完之后释放锁,交给下一个线程执行。
4. 什么是多线程之间的同步?
在多线程情况下,当多个线程共享统一资源时,不会受到其他线程的影响。
5. 什么是同步代码块?
就是将可能出现线程安全的代码包裹起来,使得同一时间只能有一个线程执行被包裹的代码,其他线程处于阻塞状态。
6. 同步代码块和同步函数的区别?
同步代码块是自定义锁(明锁)
同步函数是使用的 this 锁
7. 同步函数和静态同步函数的区别?/ 例如现在一个静态方法和一个非静态静态怎么实现同步?
同步函数使用的 this 锁;
静态同步函数使用的是字节码文件,即类.class
8. 什么是死锁?
同步中嵌套同步,俗称套娃。