java锁:synchronized、ReadWriteLock、ReentrantReadWriteLock*

1、先读这篇文章,了解synchronized:Java线程同步:synchronized锁住的是代码仍是对象java

  1. synchronizied,默认是synchronized(this),括号里的内容能够当作是锁。
  2. 把锁当成对象看待,在类C中加锁的代码块A,能锁住代码块A的锁有不少,new出C类的对象c一、c2,默认就是synchronized(c1){A}、synchronized(c2){A},在对象c1中,得到了c1的锁才能执行代码。在对象c2中,得到了c2的锁才能执行代码。相同的代码块A,在不一样的对象c一、c2中,是能够同时执行的,可是在对象c1中,A是不能执行的(c2也同理)。
  3. 在类C中加锁的代码块A,synchronized(C.class){A},这时锁是C的class对象,至关于在一个jvm中,只有一个C的class对象,因此这时候就是全局锁了,在整个jvm中,代码A只能有一个并发。
  4. 加锁的代码是会影响并发的,因此加锁的内容能缩小就尽可能缩小。
  5. 若是在一个对象中,有多个synchronized代码块A,B,C,则A如今正被锁着,B和C也会被同时锁住。

2、synchronized加锁形成的问题
对象的方法中一旦加入synchronized修饰,则这个对象任什么时候刻只能有一个线程访问synchronized修饰的方法。假设有个数据对象拥有写方法与读方法,多线程环境中要想保证数据的安全,需对该对象的读写方法都要加入 synchronized同步块。这样任何线程在写入时,其它线程没法读取与改变数据;若是有线程在读取时,其余线程也没法读取或写入。这种方式在写入操做远大于读操做时,问题不大,而当读取远远大于写入时,会形成性能瓶颈,由于此种状况下读取操做是能够同时进行的,而加锁操做限制了数据的并发读取。这就引出了ReadWriteLock,读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由jvm本身控制的,你只要上好相应的锁便可。若是你的代码只读数据,能够不少人同时读,但不能同时写,那就上读锁;若是你的代码修改数据,只能有一我的在写,且不能同时读取,那就上写锁。总之,读的时候上读锁,写的时候上写锁!
可是ReadWriteLock是个接口,ReentrantReadWriteLock是他的实现类,ReentrantReadWriteLock特性以下:安全

  • ReentrantReadWriteLock和synchronized同样,不一样的对象是不一样的锁,一个对象产生的锁,只能自己的对象。
  • 重入方面其内部的WriteLock能够获取ReadLock,可是反过来ReadLock想要得到WriteLock则永远都不要想。
  • WriteLock能够降级为ReadLock,顺序是:先得到WriteLock再得到ReadLock,而后释放WriteLock,这时候线程将保持Readlock的持有。反过来ReadLock想要升级为WriteLock则不可能。
  • ReadLock能够被多个线程持有而且在做用时排斥任何的WriteLock,而WriteLock则是彻底的互斥。这一特性最为重要,由于对于高读取频率而相对较低写入的数据结构,使用此类锁同步机制则能够提升并发量。

参考:ReentrantReadWriteLock读写锁的使用
3、ReentrantReadWriteLock实例
ReentrantReadWriteLock实现了ReadWriteLock,使用ReentrantReadWriteLock,只须要在对象O(或者某个数据结构)中定义ReentrantReadWriteLock对象便可,而后调用lock或者unlock,便可对对象O实现加锁操做。数据结构

package com.sf.log_gen;

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class LockTest {

  public static void main(String[] args) {
    Count count = new Count();
    new Thread(new Write(count)).start();
    // 由于是不一样的对象,因此这里立马就能得到ReadLock
    System.out.println(new Count().get());
    // 针对同一对象to,由于子线程先得到了对象to的WriteLock(也有可能主线程先得到,主线程最好sleep(100L)),
    // 因此这里ReadLock就须要等WriteLock释放才能得到
    System.out.println(count.get());
  }
}


class Write implements Runnable {
  Count count = null;

  public Write(Count count) {
    this.count = count;
  }

  public void run() {// 在这个线程中先得到了writeLock
    this.count.lock();
    this.count.add();
    this.count.unlock();
  }
}


class Count {
  private ReadWriteLock rwl = new ReentrantReadWriteLock();
  int num = 0;

  public void add() {
    try {
      Thread.sleep(4000L);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    num++;
  }

  public int get() {// get方法若是不加锁,则全部线程能够直接读取
    this.rwl.readLock().lock();
    int ret = num;
    this.rwl.readLock().unlock();
    return ret;
  }

  public void lock() {
    this.rwl.writeLock().lock();
  }

  public void unlock() {
    this.rwl.writeLock().unlock();
  }
}

4、悲观锁乐观锁 上面的锁都是悲观锁
乐观锁即我先对num操做,操做完了以后,在判断num是否有变化,没变化,则说明没有别的线程在对num操做,那我就能够把我操做后的值赋值给num了。多线程

相关文章
相关标签/搜索