控制并发访问资源
semaphore是一个计数器,保护访问一个或多个共享资源。当一个线程要访问一个共享资源时,首先需要获取semaphore,如果semaphore内部计数器大于0,semaphore的计数器减一,并且允许访问共享资源。计数器大于0,意味着存在free资源可以被使用。否则,如果semaphore的计数器为0,semaphore将线程置入sleep状态,直到计数器大于0。semaphore的计数器为0,意味着所有的共享资源被其他线程使用。当线程完成了共享资源,必须释放semaphore,其他线程可以访问共享线程。
Semaphore semaphore = new Semaphore(1);
public void printJob (Object document){
try {
semaphore.acquire();
long duration=(long)(Math.random()*10);
System.out.printf("%s: PrintQueue: Printing a Job during %d
seconds\n",Thread.currentThread().getName(),duration);
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}
semaphore.acquire()//阻塞其他线程,直到semaphore被释放,在阻塞期间线程可以被中断,抛出InterruptedException异常
semaphore.acquireUninterruptibly()//此版本忽略中断,不抛出异常
tryAcquire()//如果没有获得,返回false,且不会被阻塞
控制并发访问多个资源
private boolean freePrinters[] = new boolean[3];
Lock lockPrinters = new ReentrantLock();
Semaphore semaphore = new Semaphore(3);
public void printJob (Object document){
try {
semaphore.acquire();//允许同时三个线程进入访问打印机
int assignedPrinter=getPrinter();//获取打印机
long duration=(long)(Math.random()*10);
System.out.printf("%s: PrintQueue: Printing a Job in Printer
%d during %d seconds\n",Thread.currentThread().getName(),\
assignedPrinter,duration);
TimeUnit.SECONDS.sleep(duration);
freePrinters[assignedPrinter]=true;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}
private int getPrinter() {
try {
lockPrinters.lock();//
for (int i=0; i<freePrinters.length; i++) {
if (freePrinters[i]){
ret=i;
freePrinters[i]=false;
break;
}
}catch (Exception e) {
e.printStackTrace();
} finally {
lockPrinters.unlock();
}
return ret;
}
等待多个并发事件
Java提供了允许一个或多个线程等待直到一组操作完成: CountDownLatch,它用一个整数来初始化,即等待操作数目,当一个线程等待这些操作完成时,使用await()方法,此方法将线程置入sleep状态直到操作完成。当一个操作完成时调用countDown()。
CountDownLatch controller = new CountDownLatch(number);
controller.countDown();//操作线程调用
try {
controller.await();//等待线程
System.out.printf("VideoConference: All the participants
have come\n");
System.out.printf("VideoConference: Let's start...\n");
} catch (InterruptedException e) {
e.printStackTrace();
}
当创建CountDownLatch对象,使用构造器参数来初始化内部计数器,每一次线程调用countDown()方法,CountDownLatch对象的内部计数器自减1,当内部计数器为0时,CountDownLatch对象唤醒在await()方法中等待的线程。一旦计数器为0,所有调用await()方法立即返回,所有countDown()调用都不会生效。 CountDownLatch机制用多个任务来同步一个或多个线程。如果需要重新同步,需要创建新的CountDownLatch对象。
并发任务之间交换数据
Java提供了在两并行线程之间交换数据的同步组件。Exchanger类允许定义两线程之间的同步点,当两个线程到达这个点时,他们交换数据结构,第一个线程的数据结构流向第二个线程,而第二个线程的数据流向第一个线程。
public class ThreadLocalTest {
public static void main(String[] args) {
Exchanger<List<Integer>> exchanger = new Exchanger<>();
new Consumer(exchanger).start();
new Producer(exchanger).start();
}
}
class Producer extends Thread {
List<Integer> list = new ArrayList<>();
Exchanger<List<Integer>> exchanger = null;
public Producer(Exchanger<List<Integer>> exchanger) {
super();
this.exchanger = exchanger;
}
@Override
public void run() {
Random rand = new Random();
for(int i=0; i<10; i++) {
list.clear();
list.add(rand.nextInt(10000));
list.add(rand.nextInt(10000));
list.add(rand.nextInt(10000));
list.add(rand.nextInt(10000));
list.add(rand.nextInt(10000));
try {
list = exchanger.exchange(list);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
class Consumer extends Thread {
List<Integer> list = new ArrayList<>();
Exchanger<List<Integer>> exchanger = null;
public Consumer(Exchanger<List<Integer>> exchanger) {
super();
this.exchanger = exchanger;
}
@Override
public void run() {
for(int i=0; i<10; i++) {
try {
list = exchanger.exchange(list);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.print(list.get(0)+", ");
System.out.print(list.get(1)+", ");
System.out.print(list.get(2)+", ");
System.out.print(list.get(3)+", ");
System.out.println(list.get(4)+", ");
}
}
}