在编码的过程中。有时候我们不得不借助锁同步来保证线程安全。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策略,所
以各个线程可以更平均的分配时间。