当使用多线程访问同一个资源的时候,非常容易出现线程安全的问题(例如,当多个线程同时对一个数据进行修改的时候,会导致某些线程对数据的修改丢失)。
线程安全概念:当多个线程访问某一个类(对象或方法)时,这个对象始终都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的。
因此,需要采用同步机制来解决这种问题。而Java主要提供了三种实现同步机制的方法。
一、synchronized(同步代码块)
1、synchronized块既可以把任意的代码段声明为synchronized,也可以指定上锁的对象,有非常高的灵活性。其用法如下:
synchronized(syncObject){
//访问synchObject的代码
}
2、synchronized块的使用
public class MyRunnable implements Runnable{
private static final String TAG = "wq892373445";
@Override
public void run() {
runClass();
}
private void runClass() {
//synchronized (MyRunnable.class) {
synchronized (TAG ) {
for(int i = 0;i < 10;i++){
Log.d(TAG,Thread.currentThread().getName() + " run Class");
sleep();
}
//}
}
private void sleep() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Thread t0 = new Thread(new MyRunnable());
Thread t1 = new Thread(new MyRunnable());
Thread t2 = new Thread(new MyRunnable());
t0.setName("线程1");
t1.setName("线程2");
t2.setName("线程3");
t0.start();
t1.start();
t2.start();
运行结果如下:
//没有加synchronized 同步的结果。
2020-04-05 18:15:19.898 4297-4458/com.example.opencvdemo D/wq892373445: 线程3 run Class
2020-04-05 18:15:19.899 4297-4456/com.example.opencvdemo D/wq892373445: 线程2 run Class
2020-04-05 18:15:19.899 4297-4455/com.example.opencvdemo D/wq892373445: 线程1 run Class
2020-04-05 18:15:20.899 4297-4458/com.example.opencvdemo D/wq892373445: 线程3 run Class
2020-04-05 18:15:20.899 4297-4456/com.example.opencvdemo D/wq892373445: 线程2 run Class
2020-04-05 18:15:20.900 4297-4455/com.example.opencvdemo D/wq892373445: 线程1 run Class
2020-04-05 18:15:21.900 4297-4458/com.example.opencvdemo D/wq892373445: 线程3 run Class
2020-04-05 18:15:21.900 4297-4456/com.example.opencvdemo D/wq892373445: 线程2 run Class
2020-04-05 18:15:21.900 4297-4455/com.example.opencvdemo D/wq892373445: 线程1 run Class
//加synchronized 同步的结果。
2020-04-05 18:12:26.032 3755-3871/? D/wq892373445: 线程1 run Class
2020-04-05 18:12:27.032 3755-3871/com.example.opencvdemo D/wq892373445: 线程1 run Class
2020-04-05 18:12:28.032 3755-3871/com.example.opencvdemo D/wq892373445: 线程1 run Class
2020-04-05 18:12:29.033 3755-3872/com.example.opencvdemo D/wq892373445: 线程2 run Class
2020-04-05 18:12:30.035 3755-3872/com.example.opencvdemo D/wq892373445: 线程2 run Class
2020-04-05 18:12:31.036 3755-3872/com.example.opencvdemo D/wq892373445: 线程2 run Class
2020-04-05 18:12:32.037 3755-3884/com.example.opencvdemo D/wq892373445: 线程3 run Class
2020-04-05 18:12:33.038 3755-3884/com.example.opencvdemo D/wq892373445: 线程3 run Class
2020-04-05 18:12:34.039 3755-3884/com.example.opencvdemo D/wq892373445: 线程3 run Class
备注:从日志中可以看出可能会发生数据冲突问题(线程不安全问题),只能让当前一个线程进行执行。代码执行完成后释放锁,让后才能让其他线程进行执行。这样的话就可以解决线程不安全问题,当多个线程共享同一个资源,不会受到其他线程的干扰。
二、同步方法
1、一个方法使用 synchronized 关键字修饰后,当一个线程A使用这个方法时,其他线程想使用这个方法时就必须等待,直到线程A使用完该方法。
注意:
静态同步方法的锁是给该类的 类对象 加锁。
非静态同步方法其实就是给 this对象 进行加锁。
2、代码示例:
public class MyRunnable implements Runnable{
private static final String TAG = "wq892373445";
private int tickets = 10;
@Override
public void run() {
while (true) {
if (tickets > 0)
sell();
else
return;
}
}
public synchronized void sell() {
if (tickets > 0) {
Log.d(TAG,Thread.currentThread().getName() + "卖出第" + tickets + "张票");
tickets--;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
MyRunnable t = new MyRunnable();
new Thread(t, "窗口1").start();
new Thread(t, "窗口2").start();
new Thread(t, "窗口3").start();
运行结果如下:
//没有加synchronized
2020-04-05 19:15:37.247 20573-20618/? D/wq892373445: 窗口1卖出第10张票
2020-04-05 19:15:37.247 20573-20619/? D/wq892373445: 窗口2卖出第9张票
2020-04-05 19:15:37.247 20573-20620/? D/wq892373445: 窗口3卖出第8张票
2020-04-05 19:15:37.747 20573-20618/? D/wq892373445: 窗口1卖出第7张票
2020-04-05 19:15:37.747 20573-20619/? D/wq892373445: 窗口2卖出第7张票
2020-04-05 19:15:37.747 20573-20620/? D/wq892373445: 窗口3卖出第6张票
2020-04-05 19:15:38.247 20573-20618/com.example.opencvdemo D/wq892373445: 窗口1卖出第5张票
2020-04-05 19:15:38.247 20573-20620/com.example.opencvdemo D/wq892373445: 窗口3卖出第5张票
2020-04-05 19:15:38.247 20573-20619/com.example.opencvdemo D/wq892373445: 窗口2卖出第4张票
2020-04-05 19:15:38.748 20573-20620/com.example.opencvdemo D/wq892373445: 窗口3卖出第3张票
2020-04-05 19:15:38.748 20573-20619/com.example.opencvdemo D/wq892373445: 窗口2卖出第3张票
2020-04-05 19:15:38.748 20573-20618/com.example.opencvdemo D/wq892373445: 窗口1卖出第2张票
2020-04-05 19:15:39.248 20573-20620/com.example.opencvdemo D/wq892373445: 窗口3卖出第1张票
2020-04-05 19:15:39.248 20573-20618/com.example.opencvdemo D/wq892373445: 窗口1卖出第1张票
2020-04-05 19:15:39.248 20573-20619/com.example.opencvdemo D/wq892373445: 窗口2卖出第1张票
//加synchronized
2020-04-05 19:20:18.648 21231-21305/com.example.opencvdemo D/wq892373445: 窗口1卖出第10张票
2020-04-05 19:20:19.148 21231-21305/com.example.opencvdemo D/wq892373445: 窗口1卖出第9张票
2020-04-05 19:20:19.649 21231-21305/com.example.opencvdemo D/wq892373445: 窗口1卖出第8张票
2020-04-05 19:20:20.149 21231-21305/com.example.opencvdemo D/wq892373445: 窗口1卖出第7张票
2020-04-05 19:20:20.649 21231-21305/com.example.opencvdemo D/wq892373445: 窗口1卖出第6张票
2020-04-05 19:20:21.150 21231-21305/com.example.opencvdemo D/wq892373445: 窗口1卖出第5张票
2020-04-05 19:20:21.651 21231-21305/com.example.opencvdemo D/wq892373445: 窗口1卖出第4张票
2020-04-05 19:20:22.151 21231-21305/com.example.opencvdemo D/wq892373445: 窗口1卖出第3张票
2020-04-05 19:20:22.652 21231-21305/com.example.opencvdemo D/wq892373445: 窗口1卖出第2张票
2020-04-05 19:20:23.153 21231-21305/com.example.opencvdemo D/wq892373445: 窗口1卖出第1张票