java并发编程实战---25---26---27--读写锁-一周目

读写锁解读:https://www.jb51.net/article/145099.htm

package com.roocon.thread.ta3;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Demo {

	private Map<String, Object> map = new HashMap<>();


	private ReadWriteLock rwl = new ReentrantReadWriteLock();//可重入的读写锁类

	private Lock r = rwl.readLock();
	private Lock w = rwl.writeLock();

	public Object get(String key) {
		r.lock();
		System.out.println(Thread.currentThread().getName() + " read going..");
		try {
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			return map.get(key);
		} finally {
			r.unlock();
			System.out.println(Thread.currentThread().getName() + " read end..");
		}
	}

	public void put(String key, Object value) {
		w.lock();
		System.out.println(Thread.currentThread().getName() + " write going..");
		try {
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			map.put(key, value);
		} finally {
			w.unlock();
			System.out.println(Thread.currentThread().getName() + " write end..");
		}
	}

}

------------------------------------------------------25--------------------------------------------------------ta3-------------------------------------------

 读锁:是共享锁多个线程同时进入,读写互斥。

 写锁:排他锁,同一时刻只能有一个线程进入。

 读写锁的实现:

  ReadWriteLock是一个接口,由ReentrantReadWriteLock实现。

 public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
 public ReentrantReadWriteLock.ReadLock  readLock()  { return readerLock; }

  WriteLock和ReadLock是ReentrantReadWriteLock的内部类。

  开始就决定同步器是公平的还是非公平的。

 public ReentrantReadWriteLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
        readerLock = new ReadLock(this);
        writerLock = new WriteLock(this);
    }

   创建,声明两个内部类的:

    private final ReentrantReadWriteLock.ReadLock readerLock;
    /** Inner class providing writelock */
    private final ReentrantReadWriteLock.WriteLock writerLock;

   内部类实现Lock接口

 public static class ReadLock implements Lock, java.io.Serializable {

   内部类声明一个同步器,构造方法传入同步器,是外部类的同步器:

 protected ReadLock(ReentrantReadWriteLock lock) {
            sync = lock.sync;
        }

 外部类的构造方法,在这里传入的,传的是非公平的锁:

 public ReentrantReadWriteLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
        readerLock = new ReadLock(this);
        writerLock = new WriteLock(this);
    }
  public ReentrantReadWriteLock() {
        this(false);
    }

  读写锁是非公平的。不一定非要先加锁再先解锁。

  公平和非公平锁:https://www.jianshu.com/p/f95838f6ee63

  true和fasle是公平的还是非公平的。

  读锁调用readLock的lock();

public void lock() {
            sync.acquireShared(1);
        }

  调用的是外部类的acquireShared方法。这个是读锁是一把共享锁。

  再看writeLock:

  public void lock() {
            sync.acquire(1);
        }

   同样调用同步器的acquire方法。

   其实最后把加锁的方法都委托给同步器。

   最后同步器创建的是取决于公平和非公平。

   还有三个内部类,一个父类的同步器,一个公平的同步器,一个非公平的同步器:

 abstract static class Sync extends AbstractQueuedSynchronizer {

   上面那个是AQS

 static final class NonfairSync extends Sync {
static final class FairSync extends Sync {

  

 

  几个重要的方法:

  

  百度搜索JDK文档:

     

写锁有一个读锁是有多个的,读锁的个数不是0,写锁是拿不到的。 

     

  1个int类型4个字节,32位数。

  1111 1111 1111 1111 1111 1111 1111 1111,前16位表示一种锁的状态(次数),同理后16位。

   看tryAcquire方法,这个是ReentrantReadWriteLock下的SYNC的方法,写锁的lock调用tryAcquire方法:

protected final boolean tryAcquire(int acquires) {
            /*
             * Walkthrough:
             * 1. If read count nonzero or write count nonzero
             *    and owner is a different thread, fail.
             * 2. If count would saturate, fail. (This can only
             *    happen if count is already nonzero.)
             * 3. Otherwise, this thread is eligible for lock if
             *    it is either a reentrant acquire or
             *    queue policy allows it. If so, update state
             *    and set owner.
             */
            Thread current = Thread.currentThread();
            int c = getState();//总次数
            int w = exclusiveCount(c);//独占锁重入的次数
            if (c != 0) {//c!=0就是不是第一次进来所谓的重入
                // (Note: if c != 0 and w == 0 then shared count != 0)
                if (w == 0 || current != getExclusiveOwnerThread())
                    return false;//此时肯定不是当前线程
                if (w + exclusiveCount(acquires) > MAX_COUNT)//写锁重入的次数大于2^16-1=65535,写锁支持重入的次数就是65535
                    throw new Error("Maximum lock count exceeded");
                // Reentrant acquire
                setState(c + acquires);//重入成功+1
                return true;
            }
            if (writerShouldBlock() || //第一次进来
                !compareAndSetState(c, c + acquires))
                return false;
            setExclusiveOwnerThread(current);//把线程设置为当前的线程
            return true;
        }

  初始走的方法:c==0看这个方法writerShouldBlock()返回值永远是false,当前看的是非公平的。

  重入走的方法:c!=0 c是所有锁重入的次数。

  其中w是独占锁重入放入次数。

  compareAndSetState:调回cas自旋等待。

  private static final long serialVersionUID = 6317671515068378041L;

        /*
         * Read vs write count extraction constants and functions.
         * Lock state is logically divided into two unsigned shorts:
         * The lower one representing the exclusive (writer) lock hold count,
         * and the upper the shared (reader) hold count.
         */

        static final int SHARED_SHIFT   = 16;
        static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
        static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
        static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;

        /** Returns the number of shared holds represented in count  */
        static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
        /** Returns the number of exclusive holds represented in count  */
        static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }

低位存的是写锁进入的次数,高位存的是读锁。

java的移位运算符:https://www.cnblogs.com/steven520213/p/8602785.html

看tryRelease方法:

  protected final boolean tryRelease(int releases) {
            if (!isHeldExclusively())//是否是独占锁
                throw new IllegalMonitorStateException();
            int nextc = getState() - releases;
            boolean free = exclusiveCount(nextc) == 0;
            if (free)
                setExclusiveOwnerThread(null);//为0为空
            setState(nextc);//不为0记录还有进去几个
            return free;
        }

-----------------------共享锁的问题

分析。

 看tryAcquireShared方法:

protected final int tryAcquireShared(int unused) {
            /*
             * Walkthrough:
             * 1. If write lock held by another thread, fail.
             * 2. Otherwise, this thread is eligible for
             *    lock wrt state, so ask if it should block
             *    because of queue policy. If not, try
             *    to grant by CASing state and updating count.
             *    Note that step does not check for reentrant
             *    acquires, which is postponed to full version
             *    to avoid having to check hold count in
             *    the more typical non-reentrant case.
             * 3. If step 2 fails either because thread
             *    apparently not eligible or CAS fails or count
             *    saturated, chain to version with full retry loop.
             */
            Thread current = Thread.currentThread();
            int c = getState();
            if (exclusiveCount(c) != 0 &&//如果独占锁不为0有写的线程返回-1
                getExclusiveOwnerThread() != current)
                return -1;
            int r = sharedCount(c);//获取共享锁重入的次数
            if (!readerShouldBlock() &&
                r < MAX_COUNT &&
                compareAndSetState(c, c + SHARED_UNIT)) {//可以拿到锁
                if (r == 0) {
                    firstReader = current;
                    firstReaderHoldCount = 1;
                } else if (firstReader == current) {//判断是重入还是有另外的线程进来
                    firstReaderHoldCount++;
                } else {
                    HoldCounter rh = cachedHoldCounter;
                    if (rh == null || rh.tid != getThreadId(current))
                        cachedHoldCounter = rh = readHolds.get();
                    else if (rh.count == 0)
                        readHolds.set(rh);
                    rh.count++;//重入的值+1
                }
                return 1;
            }
            return fullTryAcquireShared(current);
        }

  保存每个读锁重入的次数的类。

  static final class HoldCounter {
            int count = 0;
            // Use id, not reference, to avoid garbage retention
            final long tid = getThreadId(Thread.currentThread());
        }

  如何保证线程安全?是ThreadLocal的。

static final class ThreadLocalHoldCounter
            extends ThreadLocal<HoldCounter> {
            public HoldCounter initialValue() {
                return new HoldCounter();
            }
        }

  不停的获取拿到返回值:

fullTryAcquireShared(current);

看tryReleaseShared

  protected final boolean tryReleaseShared(int unused) {
            Thread current = Thread.currentThread();
            if (firstReader == current) {
                // assert firstReaderHoldCount > 0;
                if (firstReaderHoldCount == 1)
                    firstReader = null;
                else
                    firstReaderHoldCount--;
            } else {
                HoldCounter rh = cachedHoldCounter;
                if (rh == null || rh.tid != getThreadId(current))
                    rh = readHolds.get();
                int count = rh.count;
                if (count <= 1) {
                    readHolds.remove();
                    if (count <= 0)
                        throw unmatchedUnlockException();
                }
                --rh.count;
            }
            for (;;) {
                int c = getState();
                int nextc = c - SHARED_UNIT;
                if (compareAndSetState(c, nextc))
                    // Releasing the read lock has no effect on readers,
                    // but it may allow waiting writers to proceed if
                    // both read and write locks are now free.
                    return nextc == 0;
            }
        }


---------------------26----------------------

案例分析:

如何对这个操作加锁?

重点:在1加读锁是为了在2在写锁unlock之后不让加写锁进程进来了,读锁进来。

总结:在写锁释放掉之前加读锁,防止写锁再来不能读。

锁的降级:

只有读读不是互斥的。

写锁释放完毕后所有的写线程竞争写锁。加读锁是为了不让写锁竞争降级为读锁。

写锁降级为读锁,操作就是在写锁没有释放时候,获取读锁,再释放写锁。

为什么可以这样呢:

https://segmentfault.com/q/1010000017046276/a-1020000017047583

这不就是读写锁的目的吗?写锁是排他锁,在一个线程持有写锁时,可以保证没有任何其他线程持有锁,相当于独占,所以它可以再持有读锁。但是反过来却不行,因为读锁并不是排他的,其他线程可能正在持有读锁或写锁,所以不能随意升级

锁的升级,读锁升级为写锁,在读锁没有释放的时候获取写锁再释放读锁。-----不支持锁的升级。

代码:

package com.roocon.thread.ta5;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import javax.imageio.spi.IIOServiceProvider;

public class Demo {

	private Map<String, Object> map = new HashMap<>();

	private ReadWriteLock rwl = new ReentrantReadWriteLock();

	private Lock r = rwl.readLock();
	private Lock w = rwl.writeLock();

	private volatile boolean isUpdate;

	public void readWrite() {
		r.lock(); // 为了保证isUpdate能够拿到最新的值,先读要拿到读锁
		if (isUpdate) {
			r.unlock();//写先释放读锁
			w.lock();
			map.put("xxx", "xxx");
			r.lock();
			w.unlock();
		}

		Object obj = map.get("xxx");

		System.out.println(obj);
		r.unlock();

	}

	public Object get(String key) {
		r.lock();
		System.out.println(Thread.currentThread().getName() + " 读操作在执行..");
		try {
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			return map.get(key);
		} finally {
			r.unlock();
			System.out.println(Thread.currentThread().getName() + " 读操执行完毕..");
		}
	}

	public void put(String key, Object value) {
		w.lock();
		System.out.println(Thread.currentThread().getName() + " 写操作在执行..");
		try {
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			map.put(key, value);
		} finally {
			w.unlock();
			System.out.println(Thread.currentThread().getName() + " 写操作执行完毕..");
		}
	}

}

 在读的时候不是那么纯粹,要根据状态是不是在读的时候写。

对这个加锁。

先读的话首先要加读锁。

这个是无法保证线程安全的。

------------------------------27-------ta5-------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值