zoukankan      html  css  js  c++  java
  • java concurrent之ReentrantLock

    在编码的过程中。有时候我们不得不借助锁同步来保证线程安全。synchronizedkeyword在上一篇博客中已经介绍。自从JDK5開始,加入了还有一种锁机制:ReentrantLock。

    二者的差别

    1、lock是jdk5之后代码层面实现的,synchronized是JVM层面实现的。


    2、synchronized在出现异常的时候可以自己主动释放锁。而lock必须在finally块中unlock()主动释放锁。否则会死锁。
    3、在竞争不激烈的时候synchronized的性能是比lock好一点的。可是当竞争非常激烈时synchronized的性能会相对几十倍的下降,由于lock用了新的锁机制,新的Lock机制终于归结到一个原子性操作上。

    4、synchronized无法中断一个正在等候获得锁的线程。也无法通过投票得到锁,假设不想等下去,也就没法得到锁;而lock能够。
    5、ReentrantLock能够採用FIFO的策略进行竞争,更加公平。

    基本使用方法

    先写个简单的样例看一下:

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;
    public class TestReentrantLock {
     public static void main(String[] args) {
      final ReentrantLock rLock = new ReentrantLock();
      final Condition condition = rLock.newCondition();
      ExecutorService executorService = Executors.newFixedThreadPool(5);
      Runnable opt = new Runnable() {
       @Override
       public void run() {
        rLock.lock();
        System.out.println(Thread.currentThread().getName()+"--->>lock()");
        try {
         condition.await();
        } catch (InterruptedException e) {
         e.printStackTrace();
        } finally {
         System.out.println(Thread.currentThread().getName()+"--->>unlock()");
         rLock.unlock();
        }
       }
      };
      
      for (int i = 0; i < 4; i++) {
       executorService.submit(opt);
      }
      
      Runnable release = new Runnable() {
       @Override
       public void run() {
        rLock.lock();
        try {
         Thread.sleep(2000);
         System.out.println(Thread.currentThread().getName()+"--->>signalAll()");
         condition.signalAll();
        } catch (InterruptedException e) {
         e.printStackTrace();
        } finally {
         rLock.unlock();
        }
       }
      };
      executorService.submit(release);
      executorService.shutdown();
     }
    }

    执行结果:
    pool-1-thread-1--->>lock()
    pool-1-thread-2--->>lock()
    pool-1-thread-3--->>lock()
    pool-1-thread-4--->>lock()
    pool-1-thread-5--->>signalAll()
    pool-1-thread-1--->>unlock()
    pool-1-thread-2--->>unlock()
    pool-1-thread-3--->>unlock()
    pool-1-thread-4--->>unlock()

    上面代码中有个Condition,它的三个方法await 、 signal 和 signalAll,与基类的wait、notify和notifyAll方法相相应,由于它们不能覆盖Object上的相应方法。所以就起了这三个奇葩的名字。由上面的代码能够看出ReentrantLock和synchronized使用方法是基本同样的。

    中断ReentrantLock

    实比例如以下:

    package co.etc.concurrent;
    import java.util.concurrent.locks.ReentrantLock;
    public class ReentrantLockSample {
     public static void main(String[] args) {
      testReentrantLock();
     }
     public static void testReentrantLock() {
      final SampleSupportLock support = new SampleSupportLock();
      Thread first = new Thread(new Runnable() {
       public void run() {
        try {
         support.doSomething();
        } catch (InterruptedException e) {
         e.printStackTrace();
        }
       }
      });
      Thread second = new Thread(new Runnable() {
       public void run() {
        try {
         support.doSomething();
        } catch (InterruptedException e) {
         System.out.println("InterruptedException--->>");
        }
       }
      });
      executeTest(first, second);
     }
     public static void executeTest(Thread a, Thread b) {
      a.start();
      try {
       Thread.sleep(100);
       b.start();
       Thread.sleep(1000);
       System.out.println("---->>>interrupt()");
       b.interrupt();
      } catch (InterruptedException e) {
       e.printStackTrace();
      }
     }
    }
    abstract class SampleSupport {
     protected int counter;
     public void startTheCountdown() {
      long currentTime = System.currentTimeMillis();
      for (;;) {
       long diff = System.currentTimeMillis() - currentTime;
       if (diff > 2000) {
        break;
       }
      }
     }
    }
    class SampleSupportLock extends SampleSupport {
     private final ReentrantLock lock = new ReentrantLock();
     public void doSomething() throws InterruptedException {
      lock.lockInterruptibly();
      System.out.println(Thread.currentThread().getName()
        + "doSomething()--->>");
      startTheCountdown();
      try {
       counter++;
      } finally {
       lock.unlock();
      }
      System.out.println("counter---->>>"+counter);
     }
    }

    执行结果:
    Thread-0doSomething()--->>
    ---->>>interrupt()
    InterruptedException--->>
    counter---->>>1

    执行结果表明第二个线程被中断了,这是由于我用的是lock.lockInterruptibly();在主线程中我调用了b.interrupt();二synchronized是没法做到的

    公平性

    实例代码例如以下:

    import java.util.Collection;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    public class TestFairLock {
     private static Lock fairLock = new ReentrantLock2(true);
     private static Lock unfairLock = new ReentrantLock2();
     public static void main(String[] args) {
      TestFairLock testFairLock = new TestFairLock();
      // testFairLock.unfair();
      testFairLock.fair();
     }
     public void fair() {
      System.out.println("fair version");
      for (int i = 0; i < 5; i++) {
       Thread thread = new Thread(new Job(fairLock)) {
        public String toString() {
         return getName();
        }
       };
       thread.setName("" + i);
       thread.start();
      }
      // sleep 5000ms
     }
     public void unfair() {
      System.out.println("unfair version");
      for (int i = 0; i < 5; i++) {
       Thread thread = new Thread(new Job(unfairLock)) {
        public String toString() {
         return getName();
        }
       };
       thread.setName("" + i);
       thread.start();
      }
      // sleep 5000ms
     }
     private static class Job implements Runnable {
      private Lock lock;
      public Job(Lock lock) {
       this.lock = lock;
      }
      @Override
      public void run() {
       for (int i = 0; i < 5; i++) {
        lock.lock();
        try {
         System.out.println("Thread--->>"
           + Thread.currentThread().getName());
        } finally {
         lock.unlock();
        }
       }
      }
     }
     private static class ReentrantLock2 extends ReentrantLock {
      private static final long serialVersionUID = 1773716895097002072L;
      public ReentrantLock2(boolean b) {
       super(b);
      }
      public ReentrantLock2() {
       super();
      }
      public Collection<Thread> getQueuedThreads() {
       return super.getQueuedThreads();
      }
     }
    }

    执行结果
    unfair version
    Thread--->>0
    Thread--->>0
    Thread--->>0
    Thread--->>0
    Thread--->>0
    Thread--->>1
    Thread--->>1
    Thread--->>1
    Thread--->>1
    Thread--->>1
    Thread--->>2
    Thread--->>2
    Thread--->>2
    Thread--->>2
    Thread--->>2
    Thread--->>3
    Thread--->>3
    Thread--->>3
    Thread--->>3
    Thread--->>3
    Thread--->>4
    Thread--->>4
    Thread--->>4
    Thread--->>4
    Thread--->>4

    fair version
    Thread--->>0
    Thread--->>0
    Thread--->>1
    Thread--->>3
    Thread--->>0
    Thread--->>4
    Thread--->>2
    Thread--->>1
    Thread--->>3
    Thread--->>0
    Thread--->>4
    Thread--->>2
    Thread--->>1
    Thread--->>3
    Thread--->>0
    Thread--->>4
    Thread--->>2
    Thread--->>1
    Thread--->>3
    Thread--->>4
    Thread--->>2
    Thread--->>1
    Thread--->>3
    Thread--->>4
    Thread--->>2

    从执行结果看到用ReentrantLock(boolean fair)构建的锁,相对ReentrantLock()是更公平的,当fair为true时採用的是FIFO策略,所

    以各个线程可以更平均的分配时间。

  • 相关阅读:
    run blackberry Sim&MDS4.7
    jsadd input name
    java memory
    silverlight Pivot Hearder
    eclipse tomcat server
    Monitor.Wait初探(5)
    Monitor.Wait初探(4)
    Monitor.Wait初探(2)
    解决远程注册表打不开,Cannot open HKEY_LOCAL_MACHIN…
    Windows界面自动化技术发展概要(二)
  • 原文地址:https://www.cnblogs.com/yutingliuyl/p/6709805.html
Copyright © 2011-2022 走看看