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