1.同步工具
1.同步工具(java.util.concurrent包)
1.Semaphore
Semaphore是计数信号量。
从概念上讲,Semaphore维护了一个许可集。比方说,定义一个有三个许可的Semaphore,那么在这个许可集下,使用acquire()方法前,会检查许可集,如果许可1,2,3都用了,那么就会阻塞acquire(),直到有一个许可可用,然后获得该许可。然后release()方法会让拥有许可者释放许可。
而实际上,Semaphore中并不会有真正的许可1,2,3.它有的只是一个可用许可的数目,当acquire()时,如果可用许可为0,则阻塞acquire(),而如果可用许可不为0,则许可数-1,相当于获得许可,然后不阻塞,继续acquire()后面的内容。
release()也是同样,使许可数+1,便相当于释放了许可。
这样,使用Semaphore可以用于限制访问某些资源的线程数目。(限制线程数目)
class Semaphore
{
public Semaphore(int permits);//非公平,permits个许可
public Semaphore(int permits,boolean fair)//可设置公平
//公平情况下,按线程调用acquire()的顺序,来获得许可,FIFO
void acquire();//获得许可
void release();//释放许可
}
Tips:
1.调用acquire()时无法保持同步锁.
2.设置许可数量为1时,也相当与一种互斥同步,只允许一个线程获得许可,其他线程调用acquire()时,都会阻塞。这种实现和Lock与synchronized的区别是,许可不是具体的对象,不归当前获得许可的线程所有,只要有任何线程调用了该Semaphore对象的release()方法,则许可释放,当前线程会继续运行,而acquire()处阻塞的线程会选择一个获得许可,然后恢复运行。
这种情况的话,可以用来解一些死锁,但会出现一些问题,比如就可能会有两个线程同时运行在需要获得许可的代码区,如果涉及共享数据,就有可能线程不安全。
并且不是从一开始的线程release()许可,但一开始的线程有可能最后还是会release(),那么这时候代码结构就会很乱。
3.非公平时,会出现许可刚被释放时,如果有线程刚好在此时申请许可,则会发生插队,而在公平的情况下,申请线程直接插入到等待队列的队尾,因此可以保证获得许可的顺序是线程调用acquire()的顺序。
简单测试:
public class Main
{
public static void main(String[] args)throws Exception
{
Semaphore s = new Semaphore(2);//1,3,4,5
for(int i=0;i<5;i++)
{
Thread t = new Thread(new Runnable() {
public void run() {
try {
s.acquire();
System.out.println(Thread.currentThread()+"begin");
Thread.sleep(1000);
System.out.println(Thread.currentThread()+"end");
}catch(Exception ex) {}
finally{
s.release();
}
}
});
t.start();
}
}
}
2.CyclicBarrier
CyclicBarrier是一个同步辅助工具:(可循环屏障)
它允许一组线程互相等待,直到到达某个公共屏障点(common barrier point)。
实际上即是确认屏障拦截的线程组的数目,然后在每个线程做好准备后,调用该屏障的await()方法,代表线程准备好了,直到做好准备并且调用了await()方法的线程数目达到了确认的拦截数目,则在最后一个线程await()时,打开屏障,所有准备好了的线程同时恢复。
class CyclicBarrier
{
public CyclicBarrier(int parties);//线程组数
public CyclicBarrier(int parties,Runnable barrierAction);//开放前执行参数线程
public int await();//拦截
public void reset();//重新设置组
public int getNumberWaiting();//返回当前拦截线程数
}
Tips:
1.在构造器上,初始化线程组数,并且可选传入一个Runnable对象,最后一个线程到达后,所有线程恢复前,会执行这个对象的run()方法。
2.对于失败的同步尝试,采取要么全部要么全不,如果在一组拦截中,有一个线程因为超时或者中断离开了屏障点,那么该屏障点处等待的其他线程也会以反常的方式离开。
3.reset()先中断当前的屏障点所有线程,然后重新开一屏障拦截。
4.CyclierBarrier是可循环的,因此一个屏障对象在建立好后,可以一直使用。
简单测试:
public class Main
{
public static void main(String[] args)throws Exception
{
CyclicBarrier barrier = new CyclicBarrier(5,new Runnable() {
public void run()
{
System.out.println("preparing-开始执行所有等待线程");
}
});
for(int i=0;i<10;i++)
{
Thread.sleep(1000);
Thread t = new Thread(new Runnable() {
public void run() {
System.out.println("已有"+barrier.getNumberWaiting()+"线程准备");
try{
barrier.await();
}catch(Exception ex) {}
System.out.println("execute"+Thread.currentThread());
}
});
t.start();
}
}
}
3.CountDownLatch
CountDownLatch是一个同步辅助工具,(闭锁)
在完成一组正在其他线程中执行的操作前,它允许一个或多个线程一直等待。
用给定的计数count初始化CountDownLatch,通过调用countDown()方法减少计数,在计数达到零前,await()方法会一直阻塞。之后,则会释放所有等待的线程。这种现象只出现一次,计数无法被重置。
用途:
1.将计数为1的CountDownLatch作为开关或者入口,在调用countDown()的线程打开入口之前,所有调用await()的线程都会阻塞。
2.计数为n的CountDownLatch,可以使一个(多个)线程在n个线程完成某个操作,或者一个操作完成n次前一直阻塞。
class CountDownLatch
{
public CountDownLatch(int count);//初始化计数
public void await()
public void countDown()
public long getCount()
}
简单测试:
public class Main
{
public static void main(String[] args)throws Exception
{
CountDownLatch latch = new CountDownLatch(3);
for(int i=0;i<5;i++)
{
Thread t = new Thread(new Runnable() {
public void run() {
try {
latch.await();
}catch(Exception ex) {}
System.out.println("execute"+Thread.currentThread());
}
});
System.out.println("线程"+i+"准备");
t.start();
}
for(int i=0;i<3;i++)
{
Thread t = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(1000);
}catch(Exception ex) {}
System.out.println("剩余count:"+latch.getCount());
latch.countDown();
}
});
t.start();
t.join();
}
}
}
4.Exchanger
class Exchanger<V>
{
public Exchanger();
public V exchange(V x);
public V exchange(V x, long timeout, TimeUnit unit);
}
可以在对中对元素进行配对和交换的线程的同步点。
两个线程间,通过exchange()进行配对,传入V对象,并接受对方传入的对象。可以被视为SynchronousQueue的双向形式。
简单测试:
public class Main
{
public static void main(String[] args)throws Exception
{
Exchanger<String> ex = new Exchanger();
Thread t1 = new Thread(new Runnable() {
public void run() {
try {
String s = "线程1:"+Thread.currentThread();
System.out.println(s);
String product1 = "线程1生产";
String p = ex.exchange(product1);
System.out.println(s+" "+p);
}catch(Exception ex) {}
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
try {
String s = "线程2:"+Thread.currentThread();
System.out.println(s);
String product2 = "线程2生产";
String p = ex.exchange(product2);
System.out.println(s+" "+p);
}catch(Exception ex) {}
}
});
t1.start();
Thread.sleep(1000);
t2.start();
}
}