1. Object上的锁
首先介绍一下Java中最基本的锁:Object.wait()、Object.notify()和Object.notifyAll()。
wait、notify和notifyAll方法是Object类的final native方法。所以这些方法不能被子类重写。
void notifyAll()解除所有那些在该对象上调用wait方法的线程的阻塞状态。该方法只能在同步方法或同步块内部调用。如果当前线程不是锁的持有者,该方法抛出一个IllegalMonitorStateException异常。
void notify()随机选择一个在该对象上调用wait方法的线程,解除其阻塞状态。该方法也只能在同步方法或同步块内部调用。如果当前线程不是锁的持有者,该方法抛出一个IllegalMonitorStateException异常。需要注意的是notify选择在该对象上调用wait方法的线程是随机的
void wait()、void wait(long millis)和void wait(long millis,int nanos)使线程进入等待状态,直到它被其他线程通过notify或者notifyAll唤醒。该方法只能在同步方法中调用。如果当前线程不是锁的持有者,该方法抛出一个IllegalMonitorStateException异常。
Object.wait()和Object.notify()和Object.notifyAll()必须写在synchronized方法内部或者synchronized块内部,这是因为:这几个方法要求当前正在运行Object.wait()方法的线程拥有object的对象锁。即使你确实知道当前上下文线程确实拥有了对象锁,也不能将Object.wait()这样的语句写在当前上下文中。
下面是一个简单的例子:
info.halo9pan.samples.java.thread.obj.ObjectNotify
final Object lock = new Object();
Thread waitThread = new Thread() {
@Override
public void run() {
try {
System.out.println(\"Wait Thread was started.\");
synchronized (lock) {
lock.wait();
}
System.out.println(\"Wait Thread was finished.\");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread notifyThread = new Thread() {
@Override
public void run() {
try {
System.out.println(\"Notify Thread was started.\");
System.out.println(\"Notify Thread sleep 1s.\");
Thread.sleep(1000L);
System.out.println(\"Notify Thread notify.\");
synchronized (lock) {
lock.notify();
}
System.out.println(\"Notify Thread was finished.\");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
waitThread.start();
notifyThread.start();
Wait Thread was started.
Notify Thread was started.
Notify Thread sleep 1s.
Notify Thread notify.
Notify Thread was finished.
Wait Thread was finished.
这三个方法提供了最基本的锁机制,而且这三个方法是Java的超级父类Object类的final方法,因此所有的Java对象都可以做锁。
2. Thread常用方法
在Java的多线程操作中,用得最多的还是Thread类了。
Thread类中也提供了线程控制的方法,sleep()算是用得最多的了。join()也是一个很实用的方法。Thread.join()把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的join()方法,直到线程A执行完毕后,才会继续执行线程B。主线程生成并起动了子线程,而子线程里要进行大量的耗时的运算,当主线程处理完其他的事务后,需要用到子线程的处理结果,这个时候一般就要用到join()方法了。
Thread.interrupt()也是线程控制常用的方法。但是Thread.interrupt()生效的情况比较复杂,调用Thread.interrupt()时只会在Object.wait()、Thread.join()和Thread.sleep()几个方法会主动抛出InterruptedException异常。而在其它的情况下,只是通过设置了Thread的一个标志位信息,需要程序自我进行处理。下面是Thread.interrupt()的一段示例:
info.halo9pan.samples.java.thread.obj.ThreadInterrupt
class WaitThread extends Thread {
@Override
public void run() {
System.out.println(\"Wait Thread was started.\");
int times = Integer.MIN_VALUE;
while (times++ < Integer.MAX_VALUE) {
}
System.out.println(\"Wait Thread was finished.\");
}
}
final WaitThread waitThread = new WaitThread();
Thread interruptThread = new Thread() {
@Override
public void run() {
try {
System.out.println(\"Interrupt Thread was started.\");
TimeUnit.MILLISECONDS.sleep(10L);
System.out.println(\"Wait Thread state.\" + waitThread.getState());
waitThread.interrupt();
System.out.println(\"Interrupt Thread was finished.\");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
waitThread.start();
interruptThread.start();
}
Wait Thread was started.
Interrupt Thread was started.
Wait Thread state.RUNNABLE
Interrupt Thread was finished.
Wait Thread was finished.
这里的Thread.interrupt()并不会打断线程的执行。在Object.wait()的情况下线程才会被打断:
info.halo9pan.samples.java.thread.obj.ThreadInterruptWait
final Object lock = new Object();
class WaitThread extends Thread {
@Override
public void run() {
try {
System.out.println(\"Wait Thread was started.\");
synchronized (lock) {
lock.wait();
}
System.out.println(\"Wait Thread was finished.\");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
final WaitThread waitThread = new WaitThread();
Thread interruptThread = new Thread() {
@Override
public void run() {
try {
System.out.println(\"Interrupt Thread was started.\");
TimeUnit.SECONDS.sleep(1L);
System.out.println(\"Wait Thread state.\" + waitThread.getState());
waitThread.interrupt();
System.out.println(\"Interrupt Thread was finished.\");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
waitThread.start();
interruptThread.start();
}
Wait Thread was started.
Interrupt Thread was started.
Wait Thread state.WAITING
Interrupt Thread was finished.
java.lang.InterruptedException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:503)
at info.halo9pan.samples.java.thread.obj.ThreadInterruptWait$1WaitThread.run(ThreadInterruptWait.java:16)
Thread.interrupt()设计的目的主要是用于处理线程处于block状态,比如wait(),sleep()状态就是个例子。但可以在程序设计时为支持task cancel,同样可以支持RUNNING状态。比如Thread.join()和一些支持interrupt的NIO channel设计:
info.halo9pan.samples.java.thread.obj.ThreadInterruptCheck
class WaitThread extends Thread {
@Override
public void run() {
System.out.println(\"Wait Thread was started.\");
int times = Integer.MIN_VALUE;
try {
while (times++ < Integer.MAX_VALUE) {
if (Thread.interrupted()) {
System.out.println(\"Wait Thread was interrupted.\");
throw new InterruptedException(Thread.currentThread().getName());
}
}
} catch (InterruptedException e) {
new RuntimeException(e);
}
System.out.println(\"Wait Thread was finished.\");
}
}
final WaitThread waitThread = new WaitThread();
Thread interruptThread = new Thread() {
@Override
public void run() {
try {
System.out.println(\"Interrupt Thread was started.\");
TimeUnit.MILLISECONDS.sleep(1000L);
System.out.println(\"Wait Thread state.\" + waitThread.getState());
waitThread.interrupt();
System.out.println(\"Interrupt Thread was finished.\");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
waitThread.start();
interruptThread.start();
}
Wait Thread was started.
Interrupt Thread was started.
Wait Thread state.RUNNABLE
Interrupt Thread was finished.
Wait Thread was interrupted.
Wait Thread was finished.
3. java.util.concurrent.locks.Lock
之前介绍的都是比较常规的Java锁,也是Java 5之前用得较多的锁实现。但是在Java 5中引入了全新的并发框架,在并发编程的时候,锁的选择也越来越多了。java.util.concurrent.locks.Lock就是在Java 5中引入的,主要有下面几个方法:
public interface Lock {
void lock(); //常规地获得锁
void lockInterruptibly() throws InterruptedException; //可中断地获得锁
boolean tryLock(); //尝试性获得锁,非阻塞
boolean tryLock(long time, TimeUnit unit) throws InterruptedException; //尝试性获得锁,如果超时则返回
void unlock(); //解锁
Condition newCondition(); //生成和当前锁相关的条件(队列)对象
}
而java.util.concurrent.locks.ReentrantLock是Lock的主要实现,实现了Lock和Serializable接口。主要的属性只有内部类Sync的对象属性sync,ReentrantLock类的操作实际上都落在了sync身上。除此之外,ReentrantLock是可重入锁,还有一些支持可重入的方法,这里不细说。可以说ReetrantLock是基于它的内部类Sync的对象来实现的。在ReentrantLock中,Sync有FairSync和Nonfair两个子类,而父层有AbstactOwnableSynchronizer和AbstractQueuedSynchronizer。前者实现了当前同步器被哪个线程占有的逻辑,实现了get/setExclusiveOwnerThread()方法,确定获取当前互斥锁的Thread对象。后者则是java.util.concurrent包中非常重要的类,它为并发包中的其他synchronizers提供了一组公共的基础设施。AbstractQueuedSynchronizer会在下面的章节中介绍。这里还是主要介绍ReentrantLock。
ReentrantLock三个加锁的方法中,lockInterruptibly 与 lock比较区别在于:lockInterruptibly 优先考虑响应中断,而不是响应锁定的普通获取或重入获取,而tryLock是非阻塞的,只是尝试性的加锁。下面通过三个例子来看看它们的区别:
info.halo9pan.samples.java.thread.lock.BasicReentrantLock
final Lock lock = new ReentrantLock();
class WaitThread extends Thread{
String token;
public WaitThread(String token) {
super();
this.token = token;
}
@Override
public void run() {
try {
System.out.println(\"Wait Thread \" + token + \" was started.\");
lock.tryLock();
System.out.println(\"Wait Thread \" + token + \" got the lock.\");
TimeUnit.SECONDS.sleep(4L);
System.out.println(\"Wait Thread \" + token + \" was finished.\");
lock.unlock();
} catch (InterruptedException e) {
System.out.println(\"Wait Thread \" + token + \" was interrupted.\");
}
}
}
final WaitThread oneThread = new WaitThread(\"one\");
final WaitThread twoThread = new WaitThread(\"two\");
Thread modifyThread = new Thread() {
@Override
public void run() {
try {
System.out.println(\"Interrupt Thread was started.\");
TimeUnit.SECONDS.sleep(1L);
twoThread.interrupt();
System.out.println(\"Interrupt Thread was finished.\");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
oneThread.start();
twoThread.start();
modifyThread.start();
}
Wait Thread one was started.
Interrupt Thread was started.
Wait Thread two was started.
Wait Thread one got the lock.
Interrupt Thread was finished.
Wait Thread one was finished.
Wait Thread two got the lock.
Wait Thread two was interrupted.
info.halo9pan.samples.java.thread.lock.BasicReentrantInterruptLock
lock.lockInterruptibly();
Wait Thread one was started.
Wait Thread one got the lock.
Wait Thread two was started.
Interrupt Thread was started.
Interrupt Thread was finished.
Wait Thread two was interrupted.
Wait Thread one was finished.
可以看到,在调用ReentrantLock.lockInterruptibly()加锁时,Thread two优先被中断了,而ReentrantLock.lock ()加锁时,Thread two获得了锁之后才会被中断。
info.halo9pan.samples.java.thread.lock.BasicReentrantTryLock
boolean success = lock.tryLock();
System.out.println(\"Wait Thread \" + token + \" got the lock: \" + success);
Wait Thread one was started.
Wait Thread one got the lock: true
Interrupt Thread was started.
Wait Thread two was started.
Wait Thread two got the lock: false
Interrupt Thread was finished.
Wait Thread two was interrupted.
Wait Thread one was finished.
在调用ReentrantLock.tryLock()加锁时,Thread one获得了锁,但是Thread two并没有获得锁。
4. Condition条件变量
条件变量是线程同步对象中的一种,主要用来等待某种条件的发生,条件发生后,可以唤醒等待在该条件上的一个线程,或所有线程。条件变量要与锁一起协同工作。条件变量作用于前面提到的Object.wait()、Object.notify()和Object.notifyAll()是一致的。
条件变量调用Lock.newCondition()获得一个实例,通常的调用方式如下:
info.halo9pan.samples.java.thread.lock.BasicLockCondition
final Lock lock = new ReentrantLock();
final Condition condition = lock.newCondition();
class WaitThread extends Thread{
String token;
public WaitThread(String token) {
super();
this.token = token;
}
@Override
public void run() {
try {
System.out.println(\"Wait Thread \" + token + \" was started.\");
lock.lock();
System.out.println(\"Wait Thread \" + token + \" got the lock.\");
System.out.println(\"Wait Thread \" + token + \" start to wait.\");
condition.await();
System.out.println(\"Wait Thread \" + token + \" finish waiting.\");
lock.unlock();
System.out.println(\"Wait Thread \" + token + \" was finished.\");
} catch (InterruptedException e) {
System.out.println(\"Wait Thread \" + token + \" was interrupted.\");
}
}
}
final WaitThread waitThread = new WaitThread(\"one\");
Thread modifyThread = new Thread() {
@Override
public void run() {
try {
System.out.println(\"Signal Thread was started.\");
TimeUnit.SECONDS.sleep(1L);
lock.lock();
System.out.println(\"Signal Thread got the lock.\");
condition.signal();
lock.unlock();
System.out.println(\"Signal Thread was finished.\");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
waitThread.start();
modifyThread.start();
}
值得注意的是,当Condition.await()时,隐式的将条件变量关联的Lock解锁,而使其他线程有机会获得Lock,而检查条件,并在条件满足时,等待在条件变量上。
5. java.util.concurrent.locks.ReentrantReadWriteLock
在使用某些种类的 Collection 时,可以使用 ReentrantReadWriteLock 来提高并发性。通常,在预期 collection很大,读取者线程访问它的次数多于写入者线程,并且读写操作的开销高于同步开销时,和适合使用ReadWriteLock。
而且Java 5中还提供了读写锁:ReadWriteLock。在多线程的环境下,对同一份数据进行读写,会涉及到线程安全的问题。比如在一个线程读取数据的时候,另外一个线程在写数据,而导致前后数据的不一致性;一个线程在写数据的时候,另一个线程也在写,同样也会导致线程前后看到的数据的不一致性。这时候可以在读写方法中加入互斥锁,任何时候只能允许一个线程的一个读或写操作,而不允许其他线程的读或写操作,这样是可以解决这样以上的问题,但是效率却大打折扣了。因为在真实的业务场景中,一份数据,读取数据的操作次数通常高于写入数据的操作,而线程与线程间的“读-读”操作是不涉及到线程安全的问题,没有必要加入互斥锁,只要在“读-写”,“写-写”期间上锁就行了。
对于这种情况,读写锁则最好的解决方案!读写锁的基本原则是:“读-读”不互斥,“读-写”互斥,“写-写”互斥,即在任何时候必须保证:只有一个线程在写入;线程正在读取的时候,写入操作等待;线程正在写入的时候,其他线程的写入操作和读取操作都要等待;
6. synchronized与ReentrantLock性能比较
前面提到过synchronized关键字,加上ReentrantLock,是Java里面主要的两种锁机制。ReentrantLock是Java 5的新特性,采用ReentrantLock可以完全替代替换synchronized传统的锁机制,而且采用ReentrantLock的方式更加面向对象,也更加灵活。也许有不少老文章认为ReentrantLock有比synchronized更好的性能,但是随着Java对synchronized的不断优化,synchronized在大部分的场景下已经有很好的性能,所以简单的写了一些例子测试一下,比较一下两者的性能。
info.halo9pan.samples.java.thread.lock.perf.InstantOperation
public static void main(String[] args) {
int[] numbers = {1, 2, 4, 8, 16, 2000};
int times = 1;
for (int i : numbers) {
System.out.println(\"================================================================================\");
SimpleDemo sync = new InstantOperation(i, new SyncRunner());
System.out.print(String.format(\"%-20s\", \"Using synchronize:\"));
sync.batchShow(times);
SimpleDemo lock = new InstantOperation(i, new LockRunner());
System.out.print(String.format(\"%-20s\", \"Using ReentrantLock:\"));
lock.batchShow(times);
}
}
public InstantOperation(int threadNumber, Runner runner) {
super(threadNumber, runner);
}
static class LockRunner extends Runner {
private Lock lock = new ReentrantLock();
@Override
public void doSomething(int invoker) {
lock.lock();
this.identifier++;
lock.unlock();
}
}
static class SyncRunner extends Runner {
@Override
public void doSomething(int invoker) {
synchronized (this) {
this.identifier++;
}
}
}
================================================================================
Using synchronize: Used thread: 1. Run times: 1. Spent time: 7636137ns. Average time: 7636137ns.
Using ReentrantLock:Used thread: 1. Run times: 1. Spent time: 741505ns. Average time: 741505ns.
================================================================================
Using synchronize: Used thread: 2. Run times: 1. Spent time: 1095153ns. Average time: 547576ns.
Using ReentrantLock:Used thread: 2. Run times: 1. Spent time: 1124660ns. Average time: 562330ns.
================================================================================
Using synchronize: Used thread: 4. Run times: 1. Spent time: 1261072ns. Average time: 315268ns.
Using ReentrantLock:Used thread: 4. Run times: 1. Spent time: 972424ns. Average time: 243106ns.
================================================================================
Using synchronize: Used thread: 8. Run times: 1. Spent time: 2028663ns. Average time: 253582ns.
Using ReentrantLock:Used thread: 8. Run times: 1. Spent time: 1751133ns. Average time: 218891ns.
================================================================================
Using synchronize: Used thread: 16. Run times: 1. Spent time: 3870453ns. Average time: 241903ns.
Using ReentrantLock:Used thread: 16. Run times: 1. Spent time: 3483023ns. Average time: 217688ns.
================================================================================
Using synchronize: Used thread: 2000. Run times: 1. Spent time: 489466690ns. Average time: 244733ns.
Using ReentrantLock:Used thread: 2000. Run times: 1. Spent time: 381708507ns. Average time: 190854ns.
info.halo9pan.samples.java.thread.lock.perf.ProcessOperation
public static void main(String[] args) {
int[] numbers = {1, 2, 4, 8, 16, 2000};
int times = 1;
for (int i : numbers) {
System.out.println(\"================================================================================\");
SimpleDemo sync = new ProcessOperation(i, new SyncRunner());
System.out.print(String.format(\"%-20s\", \"Using synchronize:\"));
sync.batchShow(times);
SimpleDemo lock = new ProcessOperation(i, new LockRunner());
System.out.print(String.format(\"%-20s\", \"Using ReentrantLock:\"));
lock.batchShow(times);
}
}
public ProcessOperation(int threadNumber, Runner runner) {
super(threadNumber, runner);
}
static class LockRunner extends Runner {
private Lock lock = new ReentrantLock();
@Override
public void doSomething(int invoker) {
lock.lock();
try {
TimeUnit.MICROSECONDS.sleep(2);
} catch (InterruptedException e) {
}
this.identifier++;
lock.unlock();
}
}
static class SyncRunner extends Runner {
@Override
public void doSomething(int invoker) {
synchronized (this) {
try {
TimeUnit.MICROSECONDS.sleep(2);
} catch (InterruptedException e) {
}
this.identifier++;
}
}
}
================================================================================
Using synchronize: Used thread: 1. Run times: 1. Spent time: 6863843ns. Average time: 6863843ns.
Using ReentrantLock:Used thread: 1. Run times: 1. Spent time: 722262ns. Average time: 722262ns.
================================================================================
Using synchronize: Used thread: 2. Run times: 1. Spent time: 2398133ns. Average time: 1199066ns.
Using ReentrantLock:Used thread: 2. Run times: 1. Spent time: 1552714ns. Average time: 776357ns.
================================================================================
Using synchronize: Used thread: 4. Run times: 1. Spent time: 4536268ns. Average time: 1134067ns.
Using ReentrantLock:Used thread: 4. Run times: 1. Spent time: 4403704ns. Average time: 1100926ns.
================================================================================
Using synchronize: Used thread: 8. Run times: 1. Spent time: 9560887ns. Average time: 1195110ns.
Using ReentrantLock:Used thread: 8. Run times: 1. Spent time: 9407796ns. Average time: 1175974ns.
================================================================================
Using synchronize: Used thread: 16. Run times: 1. Spent time: 18672337ns. Average time: 1167021ns.
Using ReentrantLock:Used thread: 16. Run times: 1. Spent time: 18420465ns. Average time: 1151279ns.
================================================================================
Using synchronize: Used thread: 2000. Run times: 1. Spent time: 2358474671ns. Average time: 1179237ns.
Using ReentrantLock:Used thread: 2000. Run times: 1. Spent time: 2316398302ns. Average time: 1158199ns.
InstantOperation只是做了简单的自增,而ProcessOperation模拟某些操作,中间暂停了两微秒,在Intel酷睿i3的处理器,Windows 7上做的测试。可以看出,ReentrantLock比synchronized有20%左右的性能提升。
7. synchronized与ReentrantReadWriteLock性能比较
info.halo9pan.samples.java.thread.lock.perf.InstantReadWrite
public static void main(String[] args) {
Runner sync = new SyncRunner();
Runner lock = new LockRunner();
int[] readNumber = { 2, 4, 1, 4, 2000, 1, 2000 };
int[] writeNumber = { 2, 1, 4, 4, 1, 2000, 2000 };
int times = 1;
for (int i = 0 ; i < readNumber.length; i++) {
int read = readNumber[i];
int write = writeNumber[i];
System.out.println(\"================================================================================\");
ReadWriteDemo syncDemo = new InstantReadWrite(read, write, sync);
System.out.print(String.format(\"%-32s\", \"Using synchronize:\"));
syncDemo.batchShow(times);
ReadWriteDemo lockDemo = new InstantReadWrite(read, write, lock);
System.out.print(String.format(\"%-32s\", \"Using ReentrantReadWriteLock:\"));
lockDemo.batchShow(times);
}
}
public InstantReadWrite(int read, int write, Runner runner) {
super(read, write);
setReader(runner);
setWriter(runner);
}
static class SyncRunner extends Runner{
@Override
protected String safeRead() {
String key = getRandomKey();
String s = null;
synchronized (this){
s = super.read(key);
}
return s;
}
@Override
protected String safeWrite() {
String key = getRandomKey();
String value = randomString(32);
String s = null;
synchronized (this){
s = super.write(key, value);
}
return s;
}
}
static class LockRunner extends Runner{
ReadWriteLock lock = new ReentrantReadWriteLock();
Lock readLock = lock.readLock();
Lock writeLock = lock.writeLock();
@Override
protected String safeRead() {
String key = getRandomKey();
String s = null;
readLock.lock();
s = super.read(key);
readLock.unlock();
return s;
}
@Override
protected String safeWrite() {
String key = getRandomKey();
String value = randomString(32);
String s = null;
writeLock.lock();
s = super.write(key, value);
writeLock.unlock();
return s;
}
}
================================================================================
Using synchronize: Used thread: 2/2. Run times: 1. Spent time: 4665840ns. Average time: 1166460ns.
Using ReentrantReadWriteLock: Used thread: 2/2. Run times: 1. Spent time: 1255941ns. Average time: 313985ns.
================================================================================
Using synchronize: Used thread: 4/1. Run times: 1. Spent time: 1450083ns. Average time: 290016ns.
Using ReentrantReadWriteLock: Used thread: 4/1. Run times: 1. Spent time: 1222158ns. Average time: 244431ns.
================================================================================
Using synchronize: Used thread: 1/4. Run times: 1. Spent time: 1214889ns. Average time: 242977ns.
Using ReentrantReadWriteLock: Used thread: 1/4. Run times: 1. Spent time: 29544329ns. Average time: 5908865ns.
================================================================================
Using synchronize: Used thread: 4/4. Run times: 1. Spent time: 1789192ns. Average time: 223649ns.
Using ReentrantReadWriteLock: Used thread: 4/4. Run times: 1. Spent time: 1521069ns. Average time: 190133ns.
================================================================================
Using synchronize: Used thread: 2000/1. Run times: 1. Spent time: 494456670ns. Average time: 247104ns.
Using ReentrantReadWriteLock: Used thread: 2000/1. Run times: 1. Spent time: 383064085ns. Average time: 191436ns.
================================================================================
Using synchronize: Used thread: 1/2000. Run times: 1. Spent time: 559353787ns. Average time: 279537ns.
Using ReentrantReadWriteLock: Used thread: 1/2000. Run times: 1. Spent time: 456515882ns. Average time: 228143ns.
================================================================================
Using synchronize: Used thread: 2000/2000. Run times: 1. Spent time: 1158579953ns. Average time: 289644ns.
Using ReentrantReadWriteLock: Used thread: 2000/2000. Run times: 1. Spent time: 1044899135ns. Average time: 261224ns.
info.halo9pan.samples.java.thread.lock.perf.ProcessReadWrite
public static void main(String[] args) {
Runner sync = new SyncRunner();
Runner lock = new LockRunner();
int[] readNumber = { 2, 4, 1, 4, 2000, 1, 2000 };
int[] writeNumber = { 2, 1, 4, 4, 1, 2000, 2000 };
int times = 1;
for (int i = 0 ; i < readNumber.length; i++) {
int read = readNumber[i];
int write = writeNumber[i];
System.out.println(\"================================================================================\");
ReadWriteDemo syncDemo = new ProcessReadWrite(read, write, sync);
System.out.print(String.format(\"%-32s\", \"Using synchronize:\"));
syncDemo.batchShow(times);
ReadWriteDemo lockDemo = new ProcessReadWrite(read, write, lock);
System.out.print(String.format(\"%-32s\", \"Using ReentrantReadWriteLock:\"));
lockDemo.batchShow(times);
}
}
public ProcessReadWrite(int read, int write, Runner runner) {
super(read, write);
setReader(runner);
setWriter(runner);
}
static class SyncRunner extends ProcessRunner{
@Override
protected String safeRead() {
String key = getRandomKey();
String s = null;
synchronized (this){
s = super.read(key);
}
return s;
}
@Override
protected String safeWrite() {
String key = getRandomKey();
String value = randomString(32);
String s = null;
synchronized (this){
s = super.write(key, value);
}
return s;
}
}
static class LockRunner extends ProcessRunner{
ReadWriteLock lock = new ReentrantReadWriteLock();
Lock readLock = lock.readLock();
Lock writeLock = lock.writeLock();
@Override
protected String safeRead() {
String key = getRandomKey();
String s = null;
readLock.lock();
s = super.read(key);
readLock.unlock();
return s;
}
@Override
protected String safeWrite() {
String key = getRandomKey();
String value = randomString(32);
String s = null;
writeLock.lock();
s = super.write(key, value);
writeLock.unlock();
return s;
}
}
================================================================================
Using synchronize: Used thread: 2/2. Run times: 1. Spent time: 9807628ns. Average time: 2451907ns.
Using ReentrantReadWriteLock: Used thread: 2/2. Run times: 1. Spent time: 3200789ns. Average time: 800197ns.
================================================================================
Using synchronize: Used thread: 4/1. Run times: 1. Spent time: 6286118ns. Average time: 1257223ns.
Using ReentrantReadWriteLock: Used thread: 4/1. Run times: 1. Spent time: 2699610ns. Average time: 539922ns.
================================================================================
Using synchronize: Used thread: 1/4. Run times: 1. Spent time: 27321096ns. Average time: 5464219ns.
Using ReentrantReadWriteLock: Used thread: 1/4. Run times: 1. Spent time: 5425305ns. Average time: 1085061ns.
================================================================================
Using synchronize: Used thread: 4/4. Run times: 1. Spent time: 9596380ns. Average time: 1199547ns.
Using ReentrantReadWriteLock: Used thread: 4/4. Run times: 1. Spent time: 7441139ns. Average time: 930142ns.
================================================================================
Using synchronize: Used thread: 2000/1. Run times: 1. Spent time: 2367678917ns. Average time: 1183247ns.
Using ReentrantReadWriteLock: Used thread: 2000/1. Run times: 1. Spent time: 524961877ns. Average time: 262349ns.
================================================================================
Using synchronize: Used thread: 1/2000. Run times: 1. Spent time: 2300219030ns. Average time: 1149534ns.
Using ReentrantReadWriteLock: Used thread: 1/2000. Run times: 1. Spent time: 2381686271ns. Average time: 1190248ns.
================================================================================
Using synchronize: Used thread: 2000/2000. Run times: 1. Spent time: 4661729854ns. Average time: 1165432ns.
Using ReentrantReadWriteLock: Used thread: 2000/2000. Run times: 1. Spent time: 2915582988ns. Average time: 728895ns.
InstantReadWrite只是做了简单的自增,ReentrantReadWriteLock与ReentrantLock比较接近,比synchronized有20%左右的性能提升。而ProcessReadWrite模拟某些操作,中间暂停了两微秒,在2000读线程,1个写线程的情景下,ReentrantReadWriteLock就充分体现了优势,花费的时间只有synchronized的10%!而且在2000读线程,2000写线程的情景下,平均时间也只有synchronized的60%。这个性能的提升是非常明显的。
8. java.util.concurrent.locks.LockSupport
最后再简单说说LockSupport,LockSupport主要有两个方法:park和unpark,它们主要是为了代替之前Thread中的Thread.suspend()和Thread.resume()的,这两个在Thread中的方法,Java已经不建议使用了,被标注为@Deprecated。
LockSupport的park()和unpark(),与Object.wait()和notify()的功能比较类似,但还是有区别的。首先,它们面向的主体不一样。LockSuport主要是针对Thread进进行阻塞处理,可以指定阻塞队列的目标对象,每次可以指定具体的线程唤醒。Object.wait()是以对象为基础,阻塞当前的线程和唤醒单个或者所有线程。其次,实现机制不同。虽然LockSuport可以指定观察的object对象,但和Object.wait(),两者的阻塞队列并不交叉,Object.notifyAll()不能唤醒LockSupport的阻塞线程。
LockSupport还有一个方法:park(Object)可以传递一个blocker对象,对应的blcoker会记录在Thread的一个parkBlocker属性中,通过jstack命令可以非常方便的监控具体的阻塞对象。
LockSupport.park()能响应interrupt事件,但不会抛出InterruptedException异常。并且LockSupport.park()在任何时候都会“无原因”的返回,所以通常会把它放在无限循环中通过条件判断来退出,它提供了一个比自旋锁性能更好锁机制。
注意LockSupport的park()和unpark()是基于许可的,并且任何时候只有一个许可是有效的。这意味着如果就算LockSupport.park()之前被调用了多次,而LockSupport.unpark()只需要调用一次就能获得许可。同时如果LockSupport.unpark()先于LockSupport.park()调用,LockSupport同样能获得许可而不会被阻塞。
LockSupport往往被应用于线程的框架程序中,而在应用级别的并发程序中用得较少。如果你想开发自己的并发框架,才会较多的用到LockSupport,而编写一般的多线程程序,LockSupport就不是特别合适了。