当前位置: 移动技术网 > IT编程>开发语言>Java > Java多线程并发编程(互斥锁Reentrant Lock)

Java多线程并发编程(互斥锁Reentrant Lock)

2019年07月22日  | 移动技术网IT编程  | 我要评论

java 中的锁通常分为两种:

通过关键字 synchronized 获取的锁,我们称为同步锁,上一篇有介绍到:java 多线程并发编程 synchronized 关键字
java.util.concurrent(juc)包里的锁,如通过继承接口 lock 而实现的 reentrantlock(互斥锁),继承 readwritelock 实现的 reentrantreadwritelock(读写锁)。
本篇主要介绍 reentrantlock(互斥锁)。

reentrantlock(互斥锁)

reentrantlock 互斥锁,在同一时间只能被一个线程所占有,在被持有后并未释放之前,其他线程若想获得该锁只能等待或放弃。

reentrantlock 互斥锁是可重入锁,即某一线程可多次获得该锁。

公平锁 and 非公平锁

public reentrantlock() {
    sync = new nonfairsync();
  }

  public reentrantlock(boolean fair) {
    sync = fair ? new fairsync() : new nonfairsync();
  }

由 reentrantlock 的构造函数可见,在实例化 reentrantlock 的时候我们可以选择实例化一个公平锁或非公平锁,而默认会构造一个非公平锁。

公平锁与非公平锁区别在于竞争锁时的有序与否。公平锁可确保有序性(fifo 队列),非公平锁不能确保有序性(即使也有 fifo 队列)。

然而,公平是要付出代价的,公平锁比非公平锁要耗性能,所以在非必须确保公平的条件下,一般使用非公平锁可提高吞吐率。所以 reentrantlock 默认的构造函数也是“不公平”的。

一般使用

demo1:

public class test {

  private static class counter {

    private reentrantlock mreentrantlock = new reentrantlock();

    public void count() {
      mreentrantlock.lock();
      try {
        for (int i = 0; i < 6; i++) {
          system.out.println(thread.currentthread().getname() + ", i = " + i);
        }
      } finally {
	      // 必须在 finally 释放锁
        mreentrantlock.unlock();
      }
    }
  }

  private static class mythread extends thread {

    private counter mcounter;

    public mythread(counter counter) {
      mcounter = counter;
    }

    @override
    public void run() {
      super.run();
      mcounter.count();
    }
  }

  public static void main(string[] var0) {
    counter counter = new counter();
    // 注:mythread1 和 mythread2 是调用同一个对象 counter
    mythread mythread1 = new mythread(counter);
    mythread mythread2 = new mythread(counter);
    mythread1.start();
    mythread2.start();
  }
}

demo1 输出:

thread-0, i = 0
thread-0, i = 1
thread-0, i = 2
thread-0, i = 3
thread-0, i = 4
thread-0, i = 5
thread-1, i = 0
thread-1, i = 1
thread-1, i = 2
thread-1, i = 3
thread-1, i = 4
thread-1, i = 5

demo1 仅使用了 reentrantlock 的 lock 和 unlock 来提现一般锁的特性,确保线程的有序执行。此种场景 synchronized 也适用。

锁的作用域

demo2:

public class test {

  private static class counter {

    private reentrantlock mreentrantlock = new reentrantlock();

    public void count() {
      for (int i = 0; i < 6; i++) {
        mreentrantlock.lock();
        // 模拟耗时,突出线程是否阻塞
        try{
          thread.sleep(100);
          system.out.println(thread.currentthread().getname() + ", i = " + i);
        } catch (interruptedexception e) {
          e.printstacktrace();
        } finally {
	        // 必须在 finally 释放锁
          mreentrantlock.unlock();
        }
      }
    }

    public void dootherthing(){
      for (int i = 0; i < 6; i++) {
        // 模拟耗时,突出线程是否阻塞
        try {
          thread.sleep(100);
        } catch (interruptedexception e) {
          e.printstacktrace();
        }
        system.out.println(thread.currentthread().getname() + " dootherthing, i = " + i);
      }
    }
  }
  
  public static void main(string[] var0) {
    final counter counter = new counter();
    new thread(new runnable() {
      @override
      public void run() {
        counter.count();
      }
    }).start();
    new thread(new runnable() {
      @override
      public void run() {
        counter.dootherthing();
      }
    }).start();
  }
}

demo2 输出:

thread-0, i = 0
thread-1 dootherthing, i = 0
thread-0, i = 1
thread-1 dootherthing, i = 1
thread-0, i = 2
thread-1 dootherthing, i = 2
thread-0, i = 3
thread-1 dootherthing, i = 3
thread-0, i = 4
thread-1 dootherthing, i = 4
thread-0, i = 5
thread-1 dootherthing, i = 5

demo3:

public class test {

  private static class counter {

    private reentrantlock mreentrantlock = new reentrantlock();

    public void count() {
      for (int i = 0; i < 6; i++) {
        mreentrantlock.lock();
        // 模拟耗时,突出线程是否阻塞
        try{
          thread.sleep(100);
          system.out.println(thread.currentthread().getname() + ", i = " + i);
        } catch (interruptedexception e) {
          e.printstacktrace();
        } finally {
          // 必须在 finally 释放锁
          mreentrantlock.unlock();
        }
      }
    }

    public void dootherthing(){
      mreentrantlock.lock();
      try{
        for (int i = 0; i < 6; i++) {
          // 模拟耗时,突出线程是否阻塞
          try {
            thread.sleep(100);
          } catch (interruptedexception e) {
            e.printstacktrace();
          }
          system.out.println(thread.currentthread().getname() + " dootherthing, i = " + i);
        }
      }finally {
        mreentrantlock.unlock();
      }

    }
  }

  public static void main(string[] var0) {
    final counter counter = new counter();
    new thread(new runnable() {
      @override
      public void run() {
        counter.count();
      }
    }).start();
    new thread(new runnable() {
      @override
      public void run() {
        counter.dootherthing();
      }
    }).start();
  }
}

demo3 输出:

thread-0, i = 0
thread-0, i = 1
thread-0, i = 2
thread-0, i = 3
thread-0, i = 4
thread-0, i = 5
thread-1 dootherthing, i = 0
thread-1 dootherthing, i = 1
thread-1 dootherthing, i = 2
thread-1 dootherthing, i = 3
thread-1 dootherthing, i = 4
thread-1 dootherthing, i = 5

结合 demo2 和 demo3 输出可见,锁的作用域在于 mreentrantlock,因为所来自于 mreentrantlock。

可终止等待

demo4:

public class test {

  static final int timeout = 300;

  private static class counter {

    private reentrantlock mreentrantlock = new reentrantlock();

    public void count() {
      try{
        //lock() 不可中断
        mreentrantlock.lock();
        // 模拟耗时,突出线程是否阻塞
        for (int i = 0; i < 6; i++) {
          long starttime = system.currenttimemillis();
          while (true) {
            if (system.currenttimemillis() - starttime > 100)
              break;
          }
          system.out.println(thread.currentthread().getname() + ", i = " + i);
        }
      } finally {
        // 必须在 finally 释放锁
        mreentrantlock.unlock();
      }
    }

    public void dootherthing(){
      try{
        //lockinterruptibly() 可中断,若线程没有中断,则获取锁
        mreentrantlock.lockinterruptibly();
        for (int i = 0; i < 6; i++) {
          // 模拟耗时,突出线程是否阻塞
          long starttime = system.currenttimemillis();
          while (true) {
            if (system.currenttimemillis() - starttime > 100)
              break;
          }
          system.out.println(thread.currentthread().getname() + " dootherthing, i = " + i);
        }
      } catch (interruptedexception e) {
        system.out.println(thread.currentthread().getname() + " 中断 ");
      }finally {
        // 若当前线程持有锁,则释放
        if(mreentrantlock.isheldbycurrentthread()){
          mreentrantlock.unlock();
        }
      }
    }
  }

  public static void main(string[] var0) {
    final counter counter = new counter();
    new thread(new runnable() {
      @override
      public void run() {
        counter.count();
      }
    }).start();
    thread thread2 = new thread(new runnable() {
      @override
      public void run() {
        counter.dootherthing();
      }
    });
    thread2.start();
    long start = system.currenttimemillis();
    while (true){
      if (system.currenttimemillis() - start > timeout) {
        // 若线程还在运行,尝试中断
        if(thread2.isalive()){
          system.out.println(" 不等了,尝试中断 ");
          thread2.interrupt();
        }
        break;
      }
    }
  }
}

demo4 输出:

thread-0, i = 0
thread-0, i = 1
thread-0, i = 2
不等了,尝试中断
thread-1 中断
thread-0, i = 3
thread-0, i = 4
thread-0, i = 5

线程 thread2 等待 300ms 后 timeout,中断等待成功。

若把 timeout 改成 3000ms,输出结果:(正常运行)

thread-0, i = 0
thread-0, i = 1
thread-0, i = 2
thread-0, i = 3
thread-0, i = 4
thread-0, i = 5
thread-1 dootherthing, i = 0
thread-1 dootherthing, i = 1
thread-1 dootherthing, i = 2
thread-1 dootherthing, i = 3
thread-1 dootherthing, i = 4
thread-1 dootherthing, i = 5

定时锁

demo5:

public class test {

  static final int timeout = 3000;

  private static class counter {

    private reentrantlock mreentrantlock = new reentrantlock();

    public void count() {
      try{
        //lock() 不可中断
        mreentrantlock.lock();
        // 模拟耗时,突出线程是否阻塞
        for (int i = 0; i < 6; i++) {
          long starttime = system.currenttimemillis();
          while (true) {
            if (system.currenttimemillis() - starttime > 100)
              break;
          }
          system.out.println(thread.currentthread().getname() + ", i = " + i);
        }
      } finally {
        // 必须在 finally 释放锁
        mreentrantlock.unlock();
      }
    }

    public void dootherthing(){
      try{
        //trylock(long timeout, timeunit unit) 尝试获得锁
        boolean islock = mreentrantlock.trylock(300, timeunit.milliseconds);
        system.out.println(thread.currentthread().getname() + " islock:" + islock);
        if(islock){
          for (int i = 0; i < 6; i++) {
            // 模拟耗时,突出线程是否阻塞
            long starttime = system.currenttimemillis();
            while (true) {
              if (system.currenttimemillis() - starttime > 100)
                break;
            }
            system.out.println(thread.currentthread().getname() + " dootherthing, i = " + i);
          }
        }else{
          system.out.println(thread.currentthread().getname() + " timeout");
        }
      } catch (interruptedexception e) {
        system.out.println(thread.currentthread().getname() + " 中断 ");
      }finally {
        // 若当前线程持有锁,则释放
        if(mreentrantlock.isheldbycurrentthread()){
          mreentrantlock.unlock();
        }
      }
    }
  }

  public static void main(string[] var0) {
    final counter counter = new counter();
    new thread(new runnable() {
      @override
      public void run() {
        counter.count();
      }
    }).start();
    thread thread2 = new thread(new runnable() {
      @override
      public void run() {
        counter.dootherthing();
      }
    });
    thread2.start();
  }
}

demo5 输出:

thread-0, i = 0
thread-0, i = 1
thread-0, i = 2
thread-1 islock:false
thread-1 timeout
thread-0, i = 3
thread-0, i = 4
thread-0, i = 5

trylock() 尝试获得锁,trylock(long timeout, timeunit unit) 在给定的 timeout 时间内尝试获得锁,若超时,则不带锁往下走,所以必须加以判断。

reentrantlock or synchronized

reentrantlock 、synchronized 之间如何选择?

reentrantlock 在性能上 比 synchronized 更胜一筹。

reentrantlock 需格外小心,因为需要显式释放锁,lock() 后记得 unlock(),而且必须在 finally 里面,否则容易造成死锁。
synchronized 隐式自动释放锁,使用方便。

reentrantlock 扩展性好,可中断锁,定时锁,自由控制。
synchronized 一但进入阻塞等待,则无法中断等待。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网