今天我们来探讨一下Java中的锁机制。前面我们提到,在JDK1.5之前只能通过synchronized关键字来实现同步,这个前面我们已经提到是属于独占锁,性能并不高,因此JDK1.5之后开始借助JNI实现了一套高效的锁实现!
JDK5以后锁的接口是JUC中的Lock,我们来先看一下他的相关API文档。
方法摘要 |
|
void |
lock() |
void |
lockInterruptibly() 如果当前线程未被中断,则获取锁。 如果锁可用,则获取锁,并立即返回。 如果锁不可用,出于线程调度目的,将禁用当前线程,并且在发生以下两种情况之一以前,该线程将一直处于休眠状态: l 锁由当前线程获得;或者 l 其他某个线程中断当前线程,并且支持对锁获取的中断。 如果当前线程: l 在进入此方法时已经设置了该线程的中断状态;或者 l 在获取锁时被中断,并且支持对锁获取的中断, 则将抛出 InterruptedException,并清除当前线程的已中断状态。 |
Condition |
newCondition() 在等待条件前,锁必须由当前线程保持。调用 Condition.await() 将在等待前以原子方式释放锁,并在等待返回前重新获取锁。 |
boolean |
tryLock() 如果锁可用,则获取锁,并立即返回值 true。如果锁不可用,则此方法将立即返回值 false。
此方法的典型使用语句如下:
Lock lock = ...; if (lock.tryLock()) { try { // manipulate protected state } finally { lock.unlock(); } } else { // perform alternative actions } 此用法可确保如果获取了锁,则会释放锁,如果未获取锁,则不会试图将其释放。 |
boolean |
tryLock(long time, TimeUnit unit) 如果锁可用,则此方法将立即返回值 true。如果锁不可用,出于线程调度目的,将禁用当前线程,并且在发生以下三种情况之一前,该线程将一直处于休眠状态: l锁由当前线程获得;或者 l其他某个线程中断当前线程,并且支持对锁获取的中断;或者 l已超过指定的等待时间
如果获得了锁,则返回值 true。
如果当前线程: l在进入此方法时已经设置了该线程的中断状态;或者 l在获取锁时被中断,并且支持对锁获取的中断, 则将抛出 InterruptedException,并会清除当前线程的已中断状态。 如果超过了指定的等待时间,则将返回值 false。如果 time 小于等于0,该方法将完全不等待。 |
void |
unlock() |
相对于API来说,我们并不能看出他到底的优点在哪里?我们来看一个实例
package com.yhj.lock; /** * @Described:原子int类型操作测试用例 * @author YHJ create at 2013-4-26 下午05:58:32 * @ClassNmae com.yhj.lock.AtomicIntegerTestCase */ public interface AtomicIntegerTestCase { /** * ++并返回 * @return * @Author YHJ create at 2013-4-26 下午05:39:47 */ int incrementAndGet(); /** * 取值 * @return * @Author YHJ create at 2013-4-26 下午05:39:56 */ int get(); }
package com.yhj.lock; /** * @Described:带同步的测试 * @author YHJ create at 2013-4-26 下午05:35:35 * @ClassNmae com.yhj.lock.AtomicIntegerWithLock */ public class AtomicIntegerWithSynchronized implements AtomicIntegerTestCase { private int value; private Object lock = new Object();//为保证两个方法都在同步内 采用对象同步方法 @Override public int incrementAndGet(){ synchronized(lock){ return ++value; } } @Override public int get(){ synchronized(lock){ return value; } } }
package com.yhj.lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @Described:带锁的测试 * @author YHJ create at 2013-4-26 下午05:35:35 * @ClassNmae com.yhj.lock.AtomicIntegerWithLock */ public class AtomicIntegerWithLock implements AtomicIntegerTestCase{ private int value; private Lock lock = new ReentrantLock(); @Override public int incrementAndGet(){ try { lock.lock(); return ++value; }finally{ lock.unlock(); } } @Override public int get(){ try { lock.lock(); return value; }finally{ lock.unlock(); } } }
package com.yhj.lock; public class Client { /** * 启动并等待线程结束 * @param threads * @throws InterruptedException * @Author YHJ create at 2013-4-26 下午05:57:16 */ private static void startAndWait(Thread [] threads) throws InterruptedException{ for(Thread thread:threads) thread.start(); for(Thread thread:threads) thread.join(); } /** * 准备线程数据 * @param threads * @param testCase * @param threadCount * @param loopCount * @Author YHJ create at 2013-4-26 下午06:01:25 */ private static void prepare(Thread [] threads,final AtomicIntegerTestCase testCase,int threadCount,final int loopCount){ for (int i = 0; i < threadCount; i++) { threads[i] = new Thread(){ @Override public void run() { for(int i=0;i<loopCount;++i){ testCase.incrementAndGet(); } } }; } } public static void main(String[] args) throws InterruptedException { //前期数据准备 final int threadCount = 200;//线程数目 final int loopCount = 100000;//循环次数 final AtomicIntegerWithLock lockTestCase = new AtomicIntegerWithLock();//测试对象 final AtomicIntegerWithSynchronized synchronizedtestCase = new AtomicIntegerWithSynchronized(); //第一波数据准备 Thread [] threads= new Thread[threadCount]; prepare(threads, lockTestCase, threadCount, loopCount); //第一波启动 long costTime = 0; long start = System.nanoTime(); startAndWait(threads); long end = System.nanoTime(); costTime = (end-start); //第一波输出 System.out.println("AtomicIntegerWithLock result:"+lockTestCase.get()+" costTime:"+costTime); System.out.println("=======我是分割线======="); //第二波数据准备 threads= new Thread[threadCount]; prepare(threads, synchronizedtestCase, threadCount, loopCount); //第二波启动 costTime = 0; start = System.nanoTime(); startAndWait(threads); end = System.nanoTime(); costTime = (end-start); //第二波输出 System.out.println("AtomicIntegerWithSynchronized result:"+lockTestCase.get()+" costTime:"+costTime); } }
运行结果:
AtomicIntegerWithLock result:20000000 costTime:1192257757
=======我是分割线=======
AtomicIntegerWithSynchronized result:20000000 costTime:3955951264
这个例子很简单,200个线程,每次执行10w个++操作,很清楚的大家看到,最好的执行结果都是20000000,都能正常的保证数据的一致性,另外我们还能看到一点,就是Lock消耗的时间要比synchronized少,也证明了Lock的性能是要笔synchronized好的!