6.修改锁的公平性
ReentrantLock和ReentrantReadWriteLock类的构造器都含有一个布尔参数fair,它允许你控制这两个类的行为。默认fair值是false,他称为非公平模式。在非公平模式下,当有很多线程在等待锁时,锁将选择他们中的一个来访问临界区,这个选择是没有任何约束的。如果fair值是true,则称为公平模式,锁会选择等待时间最长的访问临界区。这两种模式只适用于lock()和unlock()方法。而Lock接口的tryLock()方法没有将线程置于休眠,fair属性并不影响这个方法。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class PrintQueue {
private final Lock queueLock = new ReentrantLock(true);
public void printJob(Object object) {
queueLock.lock();
try {
long duration = (long)(Math.random()*10000);
System.out.println(Thread.currentThread().getName()+":PrintQueue: Printing a Job during "+(duration/1000)+" seconds");
Thread.sleep(duration);
} catch (InterruptedException e) {
// TODO: handle exception
}finally {
queueLock.unlock();
}
queueLock.lock();
try {
long duration = (long)(Math.random()*10000);
System.out.println(Thread.currentThread().getName()+":PrintQueue: Printing a Job during "+(duration/1000)+" seconds");
Thread.sleep(duration);
} catch (InterruptedException e) {
// TODO: handle exception
}finally {
queueLock.unlock();
}
}
}
public class Job implements Runnable{
private PrintQueue printQueue;
public Job(PrintQueue printQueue) {
this.printQueue = printQueue;
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName()+": Going to print a document");
printQueue.printJob(new Object());
System.out.println(Thread.currentThread().getName()+": The document has been printed");
}
}
public class Main {
public static void main(String[] args) {
PrintQueue printQueue = new PrintQueue();
Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(new Job(printQueue),"Thread "+i);
}
for (int i = 0; i < 10; i++) {
threads[i].start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
7.在锁中使用多条件
一个锁可能关联一个或者多个条件,这些条件通过Condition接口声明。目的是允许线程获取锁并且查看等待的某一个条件是否满足,如果不满足就挂起直到某个线程唤醒他们。Condition接口提供了挂起线程和唤起线程的机制。
public class FileMock {
private String[] content;
private int index;
public FileMock(int size, int length) {
content = new String[size];
StringBuffer buffer = new StringBuffer(length);
for (int i = 0; i < content.length; i++) {
for (int j = 0; j < length; j++) {
int indice = (int)Math.random()*255;
buffer.append((char)indice);
}
content[i] = buffer.toString();
}
index = 0;
}
public boolean hasMoreLines() {
return index<content.length;
}
public String getLine() {
if (this.hasMoreLines()) {
System.out.println("Mock: "+(content.length-index));
return content[index+1];
}
return null;
}
}
import java.util.LinkedList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Buffer {
private LinkedList<String> buffer;
private int maxSize;
private ReentrantLock lock;
private Condition lines;
private Condition space;
private boolean pendingLines;
public Buffer(int maxSize) {
this.maxSize = maxSize;
buffer = new LinkedList<>();
lock = new ReentrantLock();
lines = lock.newCondition();
space = lock.newCondition();
pendingLines = true;
}
public void insert(String line) {
lock.lock();
try {
while(buffer.size()==maxSize) {
space.await();
}
buffer.offer(line);
System.out.println(Thread.currentThread().getName()+": Inserted Line: "+buffer.size());
lines.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public String get() {
String line = null;
lock.lock();
try {
while((buffer.size()==0)&&(hasPendingLines())) {
lines.await();
}
if (hasPendingLines()) {
line = buffer.poll();
System.out.println(Thread.currentThread().getName()+": Line Readed: "+buffer.size());
space.signalAll();
}
} catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}finally {
lock.unlock();
}
return line;
}
public void setPendingLines(boolean pendingLines) {
this.pendingLines = pendingLines;
}
public boolean hasPendingLines() {
return pendingLines || buffer.size()>0;
}
}
public class Producer implements Runnable{
private FileMock mock;
private Buffer buffer;
public Producer(FileMock mock, Buffer buffer) {
this.mock = mock;
this.buffer = buffer;
}
@Override
public void run() {
// TODO Auto-generated method stub
buffer.setPendingLines(true);
while(mock.hasMoreLines()) {
String line = mock.getLine();
buffer.insert(line);
}
buffer.setPendingLines(false);
}
}
public class Consumer implements Runnable{
private Buffer buffer;
public Consumer(Buffer buffer) {
this.buffer = buffer;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(buffer.hasPendingLines()) {
String line = buffer.get();
processLine(line);
}
}
private void processLine(String line) {
try {
Random random = new Random();
Thread.sleep(random.nextInt(100));
} catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
public class Main {
public static void main(String[] args) {
FileMock mock = new FileMock(100, 10);
Buffer buffer = new Buffer(20);
Producer producer = new Producer(mock, buffer);
Thread thread1 = new Thread(producer,"Producer");
Consumer[] consumers= new Consumer[3];
Thread[] threads = new Thread[3];
for (int i = 0; i < 3; i++) {
consumers[i] = new Consumer(buffer);
threads[i] = new Thread(consumers[i],"Consumer "+i);
}
thread1.start();
for (int i = 0; i < 3; i++) {
threads[i].start();
}
}
}
与锁绑定的所有条件对象都是通过Lock接口声明的newCondition()方法创建的。在使用条件的时候,必须获取这个条件绑定的锁,所以带条件的代码必须在调用Lock对象的lock()方法和unlock()方法之间。
当线程调用条件的await()方法时,他将自动释放这个条件绑定的锁,其他某个线程才可以获取这个锁并且执行相同的操作,或者执行这个锁保护的另一个临界区代码。
Condition接口还提供了await()方法的其他形式:
await(long time,TimeUnit unit),直到发生以下情况之一前,线程将一直处于休眠状态
- 其他某个线程中断当前线程
- 其他某个线程调用了将当前线程挂起的条件的signal()或signalAll()方法
- 指定的等待时间已经过去
awaitUninterruptibly():它是不可中断的。这个线程将休眠直到其他某个线程调用了将他挂起的条件的signal()或signalAll()方法awaitUntil(Date date):直到发生以下情况之一前,线程将一直处于休眠状态
- 其他的某个线程中断当前线程
- 其他某个线程调用了将他挂起的条件的signal()或signalAll()方法
- 指定的最后期限到了
也可以将条件和读写锁一起使用