Android多线程-----并发和同步(Lock)

一、为什么需要Lock

如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:
1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有;
2)线程执行发生异常,此时JVM会让线程自动释放锁。

那么如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程便只能等待,试想一下,这多么影响程序执行效率。

因此就需要有一种机制可以不让等待的线程一直无期限地等待下去(比如只等待一定的时间或者能够响应中断),通过Lock就可以办到。通过Lock可以知道线程有没有成功获取到锁。这个是synchronized无法办到的。
 

二、synchronized的两大不足

第一大不足:由于我们没办法设置synchronized关键字在获取锁的时候等待时间,所以synchronized可能会导致线程为了加锁而无限期地处于阻塞状态。

第二大不足:使用synchronized关键字等同于使用了互斥锁,即其他线程都无法获得锁对象的访问权。这种策略对于读多写少的应用而言是很不利的,因为即使多个读者看似可以并发运行,但他们实际上还是串行的,并将最终导致并发性能的下降。

虽然synchronized已经作为一个关键字被固化在Java语言中了,但它只提供了一种相当保守的线程安全策略,且该策略开放给程序员的控制能力极弱。

三、synchronized与Lock的区别

类别 synchronized Lock
存在层次 关键字,是内置的语言实现 是一个类
发生异常 会自动释放线程占有的锁,因此不会导致死锁现象发生 如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁
锁的释放

1、以获取锁的线程执行完同步代码,释放锁

2、线程执行发生异常,jvm会让线程释放锁

3、不需要主动释放锁

1、在finally中必须释放锁,不然容易造成线程死锁

2、需要主动释放锁

锁的获取

假设A线程获得锁,B线程等待。

如果A线程阻塞,B线程会一直等待

分情况而定,Lock有多个锁获取的方式,就是可以尝试获得锁,线程可以不用一直等待
锁状态 无法判断 可以判断
响应中断 不行
锁类型 可重入 不可中断 非公平 可重入 可判断 可公平(两者皆可)
性能 少量同步

大量同步

可以提高多个线程进行读操作的效率

可重入锁
如何选择 如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized  

四、java.util.concurrent.locks包下常用的类

1、Lock是一个接口

public interface Lock {
    /**
     * 就是用来获取锁。如果锁已被其他线程获取,则进行等待;该方法是平常使用得最多的一个方法
     */
    void lock();
    /**
     * 1、用该锁的获得方式,如果线程在获取锁的阶段进入了等待,那么可以中断此线程,即中断线程的等待状态,先去做别的事
     * 2、也就使说,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,
     * 那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。
     * 3、由于lockInterruptibly()的声明中抛出了异常,所以lock.lockInterruptibly()必须放在try块中或者在调用
     * lockInterruptibly()的方法外声明抛出InterruptedException。
     * 4、当一个线程获取了锁之后,是不会被interrupt()方法中断的。
     * 而用synchronized修饰的话,当一个线程处于等待某个锁的状态,是无法被中断的,只有一直等待下去。
     */
    void lockInterruptibly() throws InterruptedException;
    /**
     * 尝试锁定
     * 注意返回类型是boolean,如果获取锁的时候锁被占用就返回false,否则返回true
     * 也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。
     */
    boolean tryLock();
    /**
     * 尝试锁定
     * 比起tryLock(),区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。
     * 如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。
     */
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    /**
     * 释放锁
     * 使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生
     */
    void unlock();
}

2、ReentrantLock

意思是“可重入锁”,是唯一实现了Lock接口的类,并且ReentrantLock提供了更多的方法。

重入锁的中断响应功能就合理地避免了这样的情况。比如,一个正在等待获取锁的线程被“告知”无须继续等待下去,就可以停止工作了。

3、如何使用Lock

public class Test {
    private ArrayList<Integer> arrayList = new ArrayList<Integer>();
    /**
     * 1、如果lock变量是局部变量,每个线程执行该方法时都会保存一个副本,那么理所当然每个线程执行到lock.lock()处获取的是不同的锁,所以就不会发生冲突。
     * 2、如果lock是全局变量,则才会发生冲突
     */
    private Lock lock = new ReentrantLock();    //注意这个地方
    public static void main(String[] args)  {
        final Test test = new Test();
         
        new Thread(){
            public void run() {
                test.insert(Thread.currentThread());
            };
        }.start();
         
        new Thread(){
            public void run() {
                test.insert(Thread.currentThread());
            };
        }.start();
    }  
     
    public void insert(Thread thread) {
        if(lock.tryLock()) {
            try {
                System.out.println(thread.getName()+"得到了锁");
                for(int i=0;i<5;i++) {
                    arrayList.add(i);
                }
            } catch (Exception e) {
                // TODO: handle exception
            }finally {
                System.out.println(thread.getName()+"释放了锁");
                lock.unlock();
            }
        } else {
            System.out.println(thread.getName()+"获取锁失败");
        }
    }
}
输出结果
Thread-0得到了锁
Thread-1获取锁失败
Thread-0释放了锁

2、公平锁

3、ReadWriteLock、ReentrantReadWriteLock

是Lock的另一种实现方式,我们已经知道了ReentrantLock是一个排他锁,同一时间只允许一个线程访问,而ReentrantReadWriteLock允许多个读线程同时访问,但不允许写线程和读线程、写线程和写线程同时访问。相对于排他锁,提高了并发性。在实际应用中,大部分情况下对共享数据(如缓存)的访问都是读操作远多于写操作,这时ReentrantReadWriteLock能够提供比排他锁更好的并发性和吞吐量。

感谢:
https://blog.csdn.net/u012403290/article/details/64910926?locationNum=11&fps=1
https://www.cnblogs.com/handsomeye/p/5999362.html

猜你喜欢

转载自blog.csdn.net/pangjl1982/article/details/85028474