Java API除了提供synchronized关键字实现同步机制外,还提供了Lock接口以及它的实现类ReentrantLock。Lock接口除了能够实现同步机制外,还提供了一个tryLock()方法的实现。这个方法会试图获得锁,如果锁已经被其他线程获取,它将返回false并继续往下执行代码。
你会发现使用Lock接口更加灵活,因为synchronized同步代码当A线程进入后,其他视图访问同步代码的线程都会被挂起直到A线程执行完毕。而Lock接口允许你通过编码的方式来决定是否实现同步代码,并且可以使用tryLock()检测锁是否被其他线程获取,如果其他线程拥有锁,那么这个线程可以选择跳过同步代码继续向下执行。
看一个示例:
我们模拟一个打印队列PrintQueue,在这个类中,我们定义一个锁new ReentrantLock(),使用一个随机数等待时间模拟打印过程,在打印开始时加锁,打印结束后在finally块中释放锁,释放锁这一步是必不可少的,不然这个锁一直会被锁定,其他线程就无法获得这个锁,就会造成了死锁。
public class PrintQueue {
private final Lock queueLock = new ReentrantLock();
public void printJob(Object document) {
try {
queueLock.lock();
long duration = (long)(Math.random() * 10000);
System.out.println(Thread.currentThread().getName() +
" PrintQueue: print a job during " + (duration/1000) + " seconds");
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
queueLock.unlock();
}
}
}
创建一个工作线程,调用打印对列类执行打印操作。
public class Job implements Runnable{
private PrintQueue printQueue;
public Job(PrintQueue printQueue) {
this.printQueue = printQueue;
}
@Override
public void run() {
System.out.printf("%s: Going to print a document.\n", Thread.currentThread().getName());
printQueue.printJob(new Object());
System.out.printf("%s: The document has been printed.\n", Thread.currentThread().getName());
}
}
创建主方法类,新建打印队列并启动10个线程来执行打印操作。
public class Job implements Runnable{
private PrintQueue printQueue;
public Job(PrintQueue printQueue) {
this.printQueue = printQueue;
}
@Override
public void run() {
System.out.printf("%s: Going to print a document.\n", Thread.currentThread().getName());
printQueue.printJob(new Object());
System.out.printf("%s: The document has been printed.\n", Thread.currentThread().getName());
}
}
查看控制台日志,发现10个打印线程几乎同时启动,但每次只有一个线程能够获取锁并执行打印操作,其他线程等待上一个线程执行完毕释放锁后继续竞争锁,获取锁的线程继续执行,其他线程继续等待,直到所有线程执行完毕。
Thread-0: Going to print a document.
Thread-0 PrintQueue: print a job during 4 seconds
Thread-9: Going to print a document.
Thread-8: Going to print a document.
Thread-7: Going to print a document.
Thread-6: Going to print a document.
Thread-5: Going to print a document.
Thread-4: Going to print a document.
Thread-3: Going to print a document.
Thread-2: Going to print a document.
Thread-1: Going to print a document.
Thread-0: The document has been printed.
Thread-9 PrintQueue: print a job during 2 seconds
Thread-9: The document has been printed.
Thread-8 PrintQueue: print a job during 2 seconds
Thread-8: The document has been printed.
Thread-7 PrintQueue: print a job during 3 seconds
Thread-7: The document has been printed.
Thread-6 PrintQueue: print a job during 8 seconds
Thread-6: The document has been printed.
Thread-5 PrintQueue: print a job during 4 seconds
Thread-5: The document has been printed.
Thread-4 PrintQueue: print a job during 9 seconds
Thread-4: The document has been printed.
Thread-3 PrintQueue: print a job during 8 seconds
Thread-3: The document has been printed.
Thread-2 PrintQueue: print a job during 7 seconds
Thread-2: The document has been printed.
Thread-1 PrintQueue: print a job during 6 seconds
Thread-1: The document has been printed.