Java7之多线程读写锁



Java7之多线程读写锁

转载自:http://www.it165.net/pro/html/201402/9242.html

ReadWriteLock是ReentrantReadWriteLock的接口,而ReentrantReadWriteLock实现类中包括子类ReadLock和WriteLock。

首先来看一下ReadWriteLock接口中方法的定义:

 

1. public interface ReadWriteLock {
2.     Lock readLock();  // 返回用于读取操作的锁
3.     Lock writeLock(); // 返回用于写入操作的锁
4. }

读取锁和写入锁不可以同时存储,且读取锁可以同时存在多个,但是写入锁只能存在一个。
来看实现类中部分变量和方法,如下:

 

 

01. private final ReentrantReadWriteLock.ReadLock readerLock; // 读锁
02. private final ReentrantReadWriteLock.WriteLock writerLock;// 写锁
03.  
04. public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
05. public ReentrantReadWriteLock.ReadLock  readLock()  { return readerLock; }
06.  
07. // 默认为非公平锁
08. public ReentrantReadWriteLock() {
09.     this(false);
10. }
11. public ReentrantReadWriteLock(boolean fair) {
12.     sync = fair ? new FairSync() : new NonfairSync();
13.     readerLock = new ReadLock(this);
14.     writerLock = new WriteLock(this);
15. }

定义了读锁和写锁变量,同时提供了两个构造函数,用来构造公平或非公平锁。

1、获取共享读锁

ReadLock内部类的实现源代码如下:

 

01. // 获取读锁,是共享锁
02. public static class ReadLock implements Lock, java.io.Serializable {
03.     private final Sync sync;
04.     protected ReadLock(ReentrantReadWriteLock lock) {
05.         sync = lock.sync;
06.     }
07.  
08.     public void lock() { // 共享读锁的获取
09.         sync.acquireShared(1);
10.     }
11.  
12.     //Acquires the read lock unless the current thread is interrupted.
13.     public void lockInterruptibly() throws InterruptedException {
14.         sync.acquireSharedInterruptibly(1);
15.     }
16.  
17.     public  boolean tryLock() {
18.         return sync.tryReadLock();
19.     }
20.  
21.     public boolean tryLock(long timeout, TimeUnit unit)
22.             throws InterruptedException {
23.         return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
24.     }
25.  
26.     public  void unlock() {
27.         sync.releaseShared(1);
28.     }
29.     public Condition newCondition() {
30.         throw new UnsupportedOperationException();
31.     }
32.     public String toString() {
33.         int r = sync.getReadLockCount();
34.         return super.toString() +
35.             "[Read locks = " + r + "]";
36.     }
37. }

读取锁是通过调用lock()方法来获取的,在这个方法中调用了acquireShared()方法,这个方法在AQS中实现,如下:

 

1. public final void acquireShared(int arg) {
2.     if (tryAcquireShared(arg) < 0)
3.         doAcquireShared(arg);
4. }

前面已经多次碰到这相同代码,不解释,直接来看tryAcquireShared()方法的实现:

 

 

01. protected final int tryAcquireShared(int unused) {
02.            Thread current = Thread.currentThread();
03.            int c = getState();        // 获取锁的状态
04.            // 如果锁是互斥锁,并且获取锁的线程不是当前线程,则返回-1,表示获取失败
05.            if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != current)
06.                return -1;
07.            int r = sharedCount(c);     // 获取读取锁的共享计数
08.            // 如果不需要阻塞等待,并且读取锁的共享计数小于MAX_COUNT;
09.            // 则通过CAS函数更新锁的状态,将读取锁的共享计数+1。
10.            if (!readerShouldBlock() && r < MAX_COUNT && compareAndSetState(c, c + SHARED_UNIT)) {
11.                if (r == 0) {           // 第1次获取读取锁
12.                    firstReader = current;
13.                    firstReaderHoldCount = 1;
14.                // 如果想要获取锁的线程(current)是第1个获取锁(firstReader)的线程
15.                } else if (firstReader == current) {
16.                    firstReaderHoldCount++;
17.                } else {
18.                    // HoldCounter是用来统计该线程获取读取锁的次数。
19.                    HoldCounter rh = cachedHoldCounter;
20.                    if (rh == null || rh.tid != current.getId())
21.                        cachedHoldCounter = rh = readHolds.get();
22.                    else if (rh.count == 0)
23.                        readHolds.set(rh);
24.                    rh.count++;         // 将该线程获取读取锁的次数+1
25.                }
26.                return 1;
27.            }
28.            return fullTryAcquireShared(current);
29.        }

tryAcquireShared()的作用是尝试获取共享锁,如果通过如上的方法获取失败,则调用fullTryAcquireShared()方法来获取:

 

 

01. final int fullTryAcquireShared(Thread current) {
02.             HoldCounter rh = null;
03.             for (;;) {
04.                 int c = getState();                           // 获取锁的状态
05.                 if (exclusiveCount(c) != 0) {                 // 写线程获取互斥锁
06.                     if (getExclusiveOwnerThread() != current) // 获取锁的线程不是当前线程
07.                         return -1;
08.                 } else if (readerShouldBlock()) { // 需要阻塞等待
09.                     if (firstReader == current) { // 当前线程是第一个线程
10.                     } else {
11.                         if (rh == null) {
12.                             rh = cachedHoldCounter;
13.                             if (rh == null || rh.tid != current.getId()) {
14.                                 rh = readHolds.get();
15.                                 if (rh.count == 0)
16.                                     readHolds.remove();
17.                             }
18.                         }
19.                         if (rh.count == 0)// 如果当前线程获取锁的计数为0,则返回-1。
20.                             return -1;
21.                     }
22.                 }
23.                 // 不需要阻塞等待,获取读取锁的共享统计数;如果共享统计数超过MAX_COUNT,则抛出异常
24.                 if (sharedCount(c) == MAX_COUNT)
25.                     throw new Error("Maximum lock count exceeded");
26.                 // 将线程获取读取锁的次数加1。
27.                 if (compareAndSetState(c, c + SHARED_UNIT)) {
28.                     // 如果是第1次获取“读取锁”,则更新firstReader和firstReaderHoldCount。
29.                     if (sharedCount(c) == 0) {
30.                         firstReader = current;
31.                         firstReaderHoldCount = 1;
32.                     // 如果想要获取锁的线程(current)是第1个获取锁(firstReader)的线程,
33.                     // 则将firstReaderHoldCount+1。
34.                     } else if (firstReader == current) {
35.                         firstReaderHoldCount++;
36.                     } else {
37.                         if (rh == null)
38.                             rh = cachedHoldCounter;
39.                         if (rh == null || rh.tid != current.getId())
40.                             rh = readHolds.get();
41.                         else if (rh.count == 0)
42.                             readHolds.set(rh);
43.                         // 更新线程的获取“读取锁”的共享计数
44.                         rh.count++;
45.                         cachedHoldCounter = rh; // cache for release
46.                     }
47.                     return 1;
48.                 }
49.             }
50.         }

doAcquireShared()定义在AQS函数中,源码如下:

01. private void doAcquireShared(int arg) {
02.     final Node node = addWaiter(Node.SHARED);
03.     boolean failed = true;
04.     try {
05.         boolean interrupted = false;
06.         for (;;) {
07.             final Node p = node.predecessor();
08.             if (p == head) {
09.                 int r = tryAcquireShared(arg);
10.                 if (r >= 0) {
11.                     setHeadAndPropagate(node, r);
12.                     p.next = null; // help GC
13.                     if (interrupted)
14.                         selfInterrupt();
15.                     failed = false;
16.                     return;
17.                 }
18.             }
19.             if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
20.                 interrupted = true;
21.         }
22.     } finally {
23.         if (failed)
24.             cancelAcquire(node);
25.     }
26. }

这段代码与前面的实现相同,在此不估过多的解释。

 

2、释放共享读锁

调用ReadLock中的unlock()方法来释放共享读锁,在这个方法中调用了如下的方法:

 

1. public final boolean releaseShared(int arg) {
2.         if (tryReleaseShared(arg)) {
3.             doReleaseShared();
4.             return true;
5.         }
6.         return false;
7.     }
01. protected final boolean tryReleaseShared(int unused) {
02.            // 获取当前线程,即释放共享锁的线程
03.            Thread current = Thread.currentThread();
04.            // 如果想要释放锁的线程(current)是第1个获取锁(firstReader)的线程,
05.            // 并且第1个获取锁的线程获取锁的次数=1,则设置firstReader为null;
06.            // 否则,将第1个获取锁的线程的获取次数-1。
07.            if (firstReader == current) {
08.                // assert firstReaderHoldCount > 0;
09.                if (firstReaderHoldCount == 1)
10.                    firstReader = null;
11.                else
12.                    firstReaderHoldCount--;
13.            } else {
14.                // 获取rh对象,并更新当前线程获取锁的信息
15.                HoldCounter rh = cachedHoldCounter;
16.                if (rh == null || rh.tid != current.getId())
17.                    rh = readHolds.get();
18.                int count = rh.count;
19.                if (count <= 1) {
20.                    readHolds.remove();
21.                    if (count <= 0)
22.                        throw unmatchedUnlockException();
23.                }
24.                --rh.count;
25.            }
26.            for (;;) {
27.                int c = getState();         // 获取锁的状态
28.                int nextc = c - SHARED_UNIT;// 将锁的获取次数-1
29.                // 通过CAS更新锁的状态
30.                if (compareAndSetState(c, nextc))
31.                    return nextc == 0;
32.            }
33.        }

 

01. private void doReleaseShared() {
02.     for (;;) {
03.         // 获取CLH队列的头节点
04.         Node h = head;
05.         // 如果头节点不为null,并且头节点不等于tail节点。
06.         if (h != null && h != tail) {
07.             // 获取头节点对应的线程的状态
08.             int ws = h.waitStatus;
09.             // 如果头节点对应的线程是SIGNAL状态,则意味着“头节点的下一个节点所对应的线程”需要被unpark唤醒。
10.             if (ws == Node.SIGNAL) {
11.                 // 设置“头节点对应的线程状态”为空状态。失败的话,则继续循环。
12.                 if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
13.                     continue;
14.                 // 唤醒“头节点的下一个节点所对应的线程”。
15.                 unparkSuccessor(h);
16.             }
17.             // 如果头节点对应的线程是空状态,则设置“文件点对应的线程所拥有的共享锁”为其它线程获取锁的空状态。
18.             else if (ws == 0 &&
19.                      !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
20.                 continue;                // loop on failed CAS
21.         }
22.         // 如果头节点发生变化,则继续循环。否则,退出循环。
23.         if (h == head)                   // loop if head changed
24.             break;
25.     }
26. }



3、读取锁的公正锁和非公平锁

公平锁和非公平锁的区别,体现在判断是否需要阻塞的函数readerShouldBlock()是不同的。公平锁的readerShouldBlock()的源码如下:

 

1. final boolean readerShouldBlock() {
2.     return hasQueuedPredecessors();
3. }

在公平共享锁中,如果在当前线程的前面有其他线程在等待获取共享锁,则返回true;否则,返回false。
非公平锁的readerShouldBlock()的源码如下:

1. final boolean readerShouldBlock() {
2.     return apparentlyFirstQueuedIsExclusive();
3. }

在非公平共享锁中,它会无视当前线程的前面是否有其他线程在等待获取共享锁。只要该非公平共享锁对应的线程不为null,则返回true

 

4、举例

 

01. public class ReadWriteLockTest1 {
02.  
03.     public static void main(String[] args) {
04.         // 创建账户
05.         MyCount myCount = new MyCount("4238920615242830", 10000);
06.         // 创建用户,并指定账户
07.         User user = new User("Tommy", myCount);
08.         // 分别启动3个“读取账户金钱”的线程 和 3个“设置账户金钱”的线程
09.         for (int i=0; i<3; i++) {
10.             user.getCash();
11.             user.setCash((i+1)*1000);
12.         }
13.     }
14. }
15.  
16. class User {
17.     private String name;            //用户名
18.     private MyCount myCount;        //所要操作的账户
19.     private ReadWriteLock myLock;   //执行操作所需的锁对象
20.  
21.     User(String name, MyCount myCount) {
22.         this.name = name;
23.         this.myCount = myCount;
24.         this.myLock = new ReentrantReadWriteLock();
25.     }
26.  
27.     public void getCash() {
28.         new Thread() {
29.             public void run() {
30.                 myLock.readLock().lock();
31.                 try {
32.                     System.out.println(Thread.currentThread().getName() +" getCash start");
33.                     myCount.getCash();
34.                     Thread.sleep(1);
35.                     System.out.println(Thread.currentThread().getName() +" getCash end");
36.                 } catch (InterruptedException e) {
37.                 } finally {
38.                     myLock.readLock().unlock();
39.                 }
40.             }
41.         }.start();
42.     }
43.  
44.     public void setCash(final int cash) {
45.         new Thread() {
46.             public void run() {
47.                 myLock.writeLock().lock();
48.                 try {
49.                     System.out.println(Thread.currentThread().getName() +" setCash start");
50.                     myCount.setCash(cash);
51.                     Thread.sleep(1);
52.                     System.out.println(Thread.currentThread().getName() +" setCash end");
53.                 } catch (InterruptedException e) {
54.                 } finally {
55.                     myLock.writeLock().unlock();
56.                 }
57.             }
58.         }.start();
59.     }
60. }
61.  
62. class MyCount {
63.     private String id;         //账号
64.     private int    cash;       //账户余额
65.  
66.     MyCount(String id, int cash) {
67.         this.id = id;
68.         this.cash = cash;
69.     }
70.  
71.     public String getId() {
72.         return id;
73.     }
74.  
75.     public void setId(String id) {
76.         this.id = id;
77.     }
78.  
79.     public int getCash() {
80.         System.out.println(Thread.currentThread().getName() +" getCash cash="+ cash);
81.         return cash;
82.     }
83.  
84.     public void setCash(int cash) {
85.         System.out.println(Thread.currentThread().getName() +" setCash cash="+ cash);
86.         this.cash = cash;
87.     }
88. }

运行的结果如下:

 

 

01. Thread-0 getCash start
02. Thread-2 getCash start
03. Thread-0 getCash cash=10000
04. Thread-2 getCash cash=10000
05. Thread-0 getCash end
06. Thread-2 getCash end
07. Thread-1 setCash start
08. Thread-1 setCash cash=1000
09. Thread-1 setCash end
10. Thread-3 setCash start
11. Thread-3 setCash cash=2000
12. Thread-3 setCash end
13. Thread-4 getCash start
14. Thread-4 getCash cash=2000
15. Thread-4 getCash end
16. Thread-5 setCash start
17. Thread-5 setCash cash=3000
18. Thread-5 setCash end
深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值