synchronized&ReentrantLock

保证线程安全的方法有很多,但是实际上我们要做的就是保证堆空间也就是JMM中的主内存的数据的安全性,这才是并发的关键。首先确保其安全,其次考虑是否有死锁、饥饿、活锁等情况会发生,进行优化。

synchronized

使用

  1. 在修饰非static方法的时候,synchronized只能确保当前修饰的this对象不会被同时修改。这个概念理解错的或不能理解的人都是对并发本身的理解有问题,并发的同步就是确保线程间修改某个堆空间(主内存)的数据不会出现安全问题,创建多个对象来尝试synchronized是根本没意义的。
  2. 修饰static方法,或者使用synchronized(A.class)的方法可以起到同时锁住堆空间所有的此类对象的效果。
  3. synchronized字段不能被继承。
  4. synchronized修饰的锁是可重入的。
  5. 中断是一种请求的状态,需要我们在代码中对其进行适当的处理。监测到语句可能会出现中断的情况后,抛出一个异常。synchronized在等待获取锁的时候不能响应中断。
  6. 使用wait来释放锁,其他线程用notify来通知可以获取锁。只有当notify/notifyAll被执行时候,才会唤醒一个或多个正处于等待状态的线程,然后继续往下执行,直到执行完synchronized代码块的代码或是中途遇到wait() ,再次释放锁。也就是说,notify/notifyAll() 的执行只是唤醒沉睡的线程,而不会立即释放锁,锁的释放要看代码块的具体执行情况。所以在编程中,尽量在使用了notify/notifyAll() 后立即退出临界区,以唤醒其他线程。 
synchronized (obj) {
       obj.wait();
       obj.notify();
       obj.notifyAll();         
 }

原理

Java 虚拟机中的同步(Synchronization)基于进入和退出管程(Monitor)对象实现, 无论是显式同步(有明确的 monitorenter 和 monitorexit 指令,即同步代码块)还是隐式同步都是如此。由方法调用指令读取运行时常量池中方法的 ACC_SYNCHRONIZED 标志来隐式实现。

ReentrantLock

使用

  1. 公平锁,在初始化的时候设置值可以让任务按照先后顺序排序,先到达的任务必定先执行,但是缺点是这样可能导致吞吐量降低。
  2. ReentrantLock实现了Lock抽象类.
  3. 需要使用lock方法显示的加锁,使用unlock方法在finally中释放锁,必须执行释放。
  4. ReentrantLock类可以唤醒指定条件的线程,而object的唤醒是随机的。
  5. 尝试获得锁:tryLock能获得锁就返回true,不能就立即返回false,tryLock(long timeout,TimeUnit unit),可以增加时间限制,如果超过该时间段还没获得锁,返回false。lock能获得锁就返回true,不能的话一直等待获得锁。
  6. 读写锁:lock.readLock().lock();lock.writeLock().lock();有读写锁的概念。
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void run() {
    lock.lock();
    try {
        condition.await();
        condition.signal();
       //do bussiness
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        lock.unlock();
    }
}

原理

AbstractQueuedSynchronizer简称AQS,是一个用于构建锁和同步容器的框架。事实上concurrent包内许多类都是基于AQS构建,例如ReentrantLock,Semaphore,CountDownLatch,ReentrantReadWriteLock,FutureTask等。AQS解决了在实现同步容器时设计的大量细节问题。AQS使用一个FIFO的队列表示排队等待锁的线程,队列头节点称作“哨兵节点”或者“哑节点”,它不与任何线程关联。其他的节点与等待线程关联,每个节点维护一个等待状态waitStatus。AQS中还有一个表示状态的字段state,例如ReentrantLocky用它表示线程重入锁的次数,Semaphore用它表示剩余的许可数量,FutureTask用它表示任务的状态。对state变量值的更新都采用CAS操作保证更新操作的原子性。

区别

  1. sync是关键字,方便;Lock是类,灵活
  2. sync执行完毕或者执行异常会从JVM层面释放锁,Lock需要手动释放锁
  3. 定时锁:Lock的定时锁tryLock可以在拿不到锁的时候释放锁
  4. Lock有公平锁的概念,sync没有
  5. Lock的lockInterruptibly方法可以在等待的时候被中断,而sync一旦等待是无法中断的
  6. ReentrantReadWriteLock有读写锁的功能
  7. 实现原理大不相同,sync是锁机制,Lock是AQS
  8. 优化sync比Lock更好一些

猜你喜欢

转载自blog.csdn.net/quinnnorris/article/details/81033755