自制Java中的Mutex类

同步问题中,一个很重要的问题是同步的域,什么是同步的域呢?简单以 synchronized 这个关键字来说,就是它所同步的范围。并发编程中很多时候出现的问题没有选好同步范围所导致的。但现有的同步关键字synchronized所能体现出来的对域的控制,估计用过的的人都应该感觉到并不是很理想。这个时候是不是很怀念Windows下所提供的Mutex操作,通过申请和释放的函数的位置控制同步的域,用起来要方便一些。

现在为了方便我们自己也可以做一个类似Windows的Mutex的类来方便我们对域的控制,下面看这个类的制作过程。

和制作上一个类一样,我们先来说一下希望这个类完成的功能:

1.调用getMutexFlag(),仅能是并发的线程中的一个继续执行,其余的阻塞。

2.调用freeMutexFlag(),执行的线程离开同步块(临界区),并能从阻塞线程中释放一个线程从getMutexFlag()中返回。

3.getMutexFlag()、freeMutexFlag(),都必须是原子操作。

上面这三个条件其实就是对Windows下Mutex的要求。

一开始我们也许会写出这样的代码:

 1 package com.choi;
 2 
 3 public class MutexFlag {
 4 
 5     protected Thread currentThread = null;
 6 
 7     public synchronized void getMutexFalg() {
 8         if (currentThread == null) {
 9             currentThread = Thread.currentThread();
10         }
11         while (currentThread != Thread.currentThread()) {
12             try {
13                 wait();
14                 if (currentThread == null) {
15                     currentThread = Thread.currentThread();
16                 }
17             } catch (Exception e) {
18             }
19         }
20     }
21     
22     public synchronized void freeMutexFlag(){
23         if(currentThread!=null){
24             currentThread = null;
25             try {
26                 notify();
27             } catch (Exception e) {
28             }
29         }
30     }
31 }

这段代码看起来是没有问题的,其实这段代码运行起来确实是没有问题的,但我们能把它提供给我们的项目组用吗?答案是不能的,我们来考虑一种情况:

项目组有三个程序员A,B,C。 A用这个类写了一个函数funA(get到Mutex未释放),B用这个类写了一个函数funB(get到Mutex未释放),C调用了funA,funB,用在一个线程中了。则这个线程运行到funA时得到了Mutex,运行到funB时再去申请Mutex能申请到吗?通过分析代码执行,肯定是NO。并申请不到,因为第一个还没有释放。Java里面如果是两个synchronized嵌套,则没有问题,第一个得到锁后,第二个如申请的是同一个锁,就会自动判断为已申请到。

我们可以用一个变量来是我们的MutexFlag实现synchronized的嵌套解决方案:

 1 package com.choi;
 2 
 3 public class MutexFlag {
 4 
 5     protected Thread currentThread = null;
 6     protected int count = 0;
 7 
 8     public synchronized void getMutexFalg() {
 9         while (tryGetMutexFlag() == false) {
10             try {
11                 wait();
12             } catch (InterruptedException e) {
13                 e.printStackTrace();
14             }
15         }
16     }
17 
18     public synchronized boolean tryGetMutexFlag() {
19         if (currentThread == null) {
20             currentThread = Thread.currentThread();
21             count = 1;
22             return true;
23         }
24         if (currentThread == Thread.currentThread()) {
25             count++;
26             return true;
27         }
28         return false;
29     }
30 
31     public synchronized void freeMutexFlag() {
32         if (currentThread == Thread.currentThread()) {
33             count--;
34         }
35         if (count == 0) {
36             currentThread = null;
37             notify();
38         }
39     }
40 }

为了编写方便我把判断获取MutexFlag的代码写到了同步的tryGetMutexFlag中了,没申请成功一次就对count做加一操作,每一free都对count做减一操作,那么当count从新等于零的时候我们就去释放一个线程获取MutexFlag。

哦,有人说如果没有调用get就调用free会有问题,这里说明一下没有问题的,因为如果没有get,currentThread是等于null的,所以count==0,然后就会调用notify,其实没有问题啦,测试发现,notify不会抛出人异常,相当于空操作。

欢迎到我的GitHub主页获取代码:https://github.com/choitony/Java-MutexFlag-Class/tree/master

原文地址:https://www.cnblogs.com/chaiwentao/p/4680836.html