Android-多线程安全问题-synchronized

时间:2023-03-08 17:26:30

先看一个售票案例Demo,多线程程序对共享数据操作引发的安全问题:

package android.java.thread09;

/**
* 售票线程
*/
class Booking implements Runnable { /**
* 模拟票的总算 10张票
*/
private int ticket = 10; @Override
public void run() { while (true) { if (ticket > 0) {
// 让线程在这里停一下,会更加容易复现线程的安全问题,就算不加这行代码,安全问题依然有
try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("名称:" + Thread.currentThread().getName() + "窗口卖出第" + ticket + "张票");
ticket--;
} } }
} /**
* 售票案例
*/
public class BookingTest { public static void main(String[] args) { /**
* 定义Runnable实现类Booking,此实现类Booking不是线程,此实现类Booking给四个Thread去执行的
*/
Runnable booking = new Booking(); // 实例化线程对象
Thread thread1 = new Thread(booking); // 此实现类Booking给Thread去执行的
Thread thread2 = new Thread(booking); // 此实现类Booking给Thread去执行的
Thread thread3 = new Thread(booking); // 此实现类Booking给Thread去执行的
Thread thread4 = new Thread(booking); // 此实现类Booking给Thread去执行的 // 开启启动线程
thread1.start(); // 启动第Thread-0窗口 执行卖票任务
thread2.start(); // 启动第Thread-1窗口 执行卖票任务
thread3.start(); // 启动第Thread-2窗口 执行卖票任务
thread4.start(); // 启动第Thread-3窗口 执行卖票任务 } }

打印的日志结果,注意:⚠️ 没有打印的日志结果都不同,这是CPU对线程非常快速的切换造成的,哪个线程先有执行权 就执行哪个线程 都是随机的

名称:Thread-0窗口卖出第10张票
名称:Thread-3窗口卖出第9张票
名称:Thread-1窗口卖出第8张票
名称:Thread-2窗口卖出第7张票
名称:Thread-0窗口卖出第6张票
名称:Thread-3窗口卖出第5张票
名称:Thread-2窗口卖出第4张票
名称:Thread-1窗口卖出第4张票
名称:Thread-2窗口卖出第2张票
名称:Thread-0窗口卖出第1张票
名称:Thread-3窗口卖出第1张票
名称:Thread-1窗口卖出第-1张票
名称:Thread-2窗口卖出第-2张票

CPU的随机性,到底切换到哪个线程,到底执行哪个线程代码的多少行,等等,都是随机的

Android-多线程安全问题-synchronized 

分析原因,为什么会出现以上日志打印的各个情况呢,为什么会出现 0张票 -1张票   这种情况呢?,看以下CPU执行线程的随机性就明白了

Android-多线程安全问题-synchronized


通过以上画图分析原因,造成安全问题的有以下两个因素:

  1.线程任务中,发现有共享数据,例如:ticket。

  2.多线程操作共享数据,例如:ticket。

造成的原因是:Thread-0在对共享数据操作过程中,CPU执行了Thread-1对共享数据操作,   相当于:我在数钱,突然我出去有事了,然后有个人拿了500块钱,等我在回来数钱的时候,就已经发生是数据安全问题


解决多线程安全问题,synchronize 加同步代码块,同步代码块:synchronize{ 操作共享数据的代码 }

Android-多线程安全问题-synchronized

package android.java.thread09;

/**
* 售票线程
*/
class Booking implements Runnable { /**
* 模拟票的总算 10张票
*/
private int ticket = 10; @Override
public void run() { while (true) { /**
* 加入了同步代码块的代码synchronized,
* 不管CPU如何疯狂的切换执行,
* 只要同步代码块里面的代码没有执行完,
* 就不准其他线程进来执行
* 这样就保证了多线程操作共享数据的安全新
*/
synchronized (Booking.class) { // 同步操作共享数据的代码 if (ticket > 0) { // 让线程在这里停一下,会更加容易复现线程的安全问题,就算不加这行代码,安全问题依然有
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println("名称:" + Thread.currentThread().getName() + "窗口卖出第" + ticket + "张票");
ticket--;
}
} } }
} /**
* 售票案例
*/
public class BookingTest { public static void main(String[] args) { /**
* 定义Runnable实现类Booking,此实现类Booking不是线程,此实现类Booking给四个Thread去执行的
*/
Runnable booking = new Booking(); // 实例化线程对象
Thread thread1 = new Thread(booking); // 此实现类Booking给Thread去执行的
Thread thread2 = new Thread(booking); // 此实现类Booking给Thread去执行的
Thread thread3 = new Thread(booking); // 此实现类Booking给Thread去执行的
Thread thread4 = new Thread(booking); // 此实现类Booking给Thread去执行的 // 开启启动线程
thread1.start(); // 启动第Thread-0窗口 执行卖票任务
thread2.start(); // 启动第Thread-1窗口 执行卖票任务
thread3.start(); // 启动第Thread-2窗口 执行卖票任务
thread4.start(); // 启动第Thread-3窗口 执行卖票任务 } }

以下日志结果,是CPU随机执行到哪个线程,就哪个线程打印,CPU执行线程的随机性很重要

Android-多线程安全问题-synchronized

以下日志结果,是CPU随机执行到哪个线程,就哪个线程打印,CPU执行线程的随机性很重要

Android-多线程安全问题-synchronized

以下日志结果,是CPU随机执行到哪个线程,就哪个线程打印,CPU执行线程的随机性很重要

Android-多线程安全问题-synchronized

.........