Android 多线程保证操作同步(同步锁的俩种)

时间:2024-04-14 08:05:56

今天来介绍一下android中多线程同步的机制


首先我们来创建几个多线程,模仿一下文件读写的操作。


private void writeLog() {
    for (int i = 0; i < 3; i++) {
        try {
            Log.e(TAG, "showLog: " + Thread.currentThread().getName() + "写入中");
            Thread.sleep(new Random().nextInt(1000));
            Log.e(TAG, "showLog: " + Thread.currentThread().getName() + "写入完成");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

private void readLog() {
    for (int i = 0; i < 3; i++) {
        try {
            Log.e(TAG, "showLog: " + Thread.currentThread().getName() + "读取中");
            Thread.sleep(new Random().nextInt(1000));
            Log.e(TAG, "showLog: " + Thread.currentThread().getName() + "读取完成");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

这俩个方法分别是模拟写入和读取的操作,由于读写操作一般都是有耗时的操作,所以这里使用sleep来代替时间。


 

接下来创建俩个Runnable的实现类。


class StuThread1 implements Runnable {
    @Override
    public void run() {
        writeLog();
    }
}

class StuThread2 implements Runnable {
    @Override
    public void run() {
        readLog();
    }
}

第一个进行写入,第二个进行读取。


接下来来循环创建线程


StuThread1 stuThread1 = new StuThread1();
StuThread2 stuThread2 = new StuThread2();
for (int i = 0; i < 2; i++) {
    new Thread(stuThread1).start();
    new Thread(stuThread2).start();
}

这时运行的结果如下


showLog: Thread-7写入中
showLog: Thread-8读取中
showLog: Thread-5写入中
showLog: Thread-6读取中
showLog: Thread-7写入完成
showLog: Thread-7写入中
showLog: Thread-8读取完成
showLog: Thread-8读取中
showLog: Thread-7写入完成
showLog: Thread-7写入中
showLog: Thread-6读取完成
showLog: Thread-6读取中
showLog: Thread-6读取完成
showLog: Thread-6读取中
showLog: Thread-5写入完成
showLog: Thread-5写入中
showLog: Thread-7写入完成
showLog: Thread-6读取完成
showLog: Thread-8读取完成
showLog: Thread-8读取中
showLog: Thread-8读取完成
showLog: Thread-5写入完成
showLog: Thread-5写入完成



很明显这时的线程是不同步的,如果真的在进行读写操作一定出现数据混乱(如果这里面的操作要是与钱有关,那你就摊上大事了....)


现在来介绍一下俩种同步的方式

1.使用synchronized关键字。

2.使用lock这个锁的对象。




synchronized这个关键字可以直接加到方法上也可使用synchronized代码块,将需要同步的代码放到这个代码块中,如:


private synchronized void writeLog() {
    for (int i = 0; i < 3; i++) {
        try {
            Log.e(TAG, "showLog: " + Thread.currentThread().getName() + "写入中");
            Thread.sleep(new Random().nextInt(1000));
            Log.e(TAG, "showLog: " + Thread.currentThread().getName() + "写入完成");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

private void readLog() {
    synchronized (this) {
        for (int i = 0; i < 3; i++) {
            try {
                Log.e(TAG, "showLog: " + Thread.currentThread().getName() + "读取中");
                Thread.sleep(new Random().nextInt(1000));
                Log.e(TAG, "showLog: " + Thread.currentThread().getName() + "读取完成");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

这俩种方式的作用是一样的。这时加了同步锁的输出结果为:


Android 多线程保证操作同步(同步锁的俩种)


这时的输出结果就是我想要的结果。


使用synchronized关键字修饰的方法会同步执行,必须要等上一个synchronized执行完才会继续执行,如果在执行过程中线程阻塞,他就会一直保持等待的状态,在这里不特别推荐使用这种方式,如果你的需求是必须保证数据同步的情况下且保证不会出现其他情况使用户无法完成操作的情况下可以使用时,可以使用。这个关键字一般用于单例模式保证线程安全时使用。无需手动释放或关闭,当操作完成后自动释放引用


2.Lock方式


首先我们要创建一个Lock的对象


private Lock lock = new ReentrantLock();

这里我们创建的是一个ReentrantLock的对象 ,具体为什么创建这个对象我也不是很清楚。


这里也有其他实现类,这个lock本身是一个接口。他的实现类有三个

Android 多线程保证操作同步(同步锁的俩种)

这里就不做具体介绍了。这里主要介绍一下使用的方式


private void writeLog() {
    for (int i = 0; i < 3; i++) {
        try {
            lock.lock();
            Log.e(TAG, "showLog: " + Thread.currentThread().getName() + "写入中");
            Thread.sleep(new Random().nextInt(1000));
            Log.e(TAG, "showLog: " + Thread.currentThread().getName() + "写入完成");
        } catch (InterruptedException e) {
            e.printStackTrace();
            Log.e(TAG, "writeLog: " + lock.tryLock());
        } finally {
            lock.unlock();
        }
    }
}


创建对象后使用lock.lock()方法进行加锁,注意:这个lock必须要在finally代码块中进行unlock()的操作。如果不进行手动释放这锁会一直存在引用,会造成很多意想不到的问题。具体的问题就不多说了,比如可能频繁GC,严重就会ANR。