JDK并发包之同步控制
一、重入锁
重入锁使用java.util.concurrent.locks.ReentrantLock来实现。示例代码如下:
public class TryReentrantLock implements Runnable{ static ReentrantLock lock=new ReentrantLock(); static int i=0; @Override public void run() { for(int j=0;j<10000;j++){ lock.lock(); try{ i++; }finally { lock.unlock(); } //重入锁可以反复进入,当然这种进入仅限于一个线程!因此可以如下: /*lock.lock(); lock.lock(); try{ i++; }finally { lock.unlock(); lock.unlock(); }*/ } } public static void main(String[] args) throws InterruptedException { TryReentrantLock l=new TryReentrantLock(); Thread t1=new Thread(l); Thread t2=new Thread(l); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(i); } }
对于synchronized来说,如果一个线程在等待锁,那么结果只有两种情况,要么它获得这把锁继续执行,要么继续等待锁。而对于使用重入锁的线程来说则不同,线程在等待锁的过程中,程序可以根据需要取消对锁的请求。
public class IntLock implements Runnable{ public static ReentrantLock lock1=new ReentrantLock(); public static ReentrantLock lock2=new ReentrantLock(); int state; /*控制加锁顺序,便于产生死锁。*/ public IntLock(int state){ this.state=state; } @Override public void run() { try{ if(state==1){ lock1.lockInterruptibly(); //设置可中断的锁 try{ Thread.sleep(500); //便于产生死锁 }catch (InterruptedException e){ e.printStackTrace(); } lock2.lockInterruptibly(); }else{ lock2.lockInterruptibly(); try{ Thread.sleep(500); }catch (InterruptedException e){ e.printStackTrace(); } lock1.lockInterruptibly(); } }catch (Exception e){ e.printStackTrace(); }finally { if(lock1.isHeldByCurrentThread()) lock1.unlock(); if(lock2.isHeldByCurrentThread()) lock2.unlock(); System.out.println(Thread.currentThread().getName()+" :quit!"); } } public static void main(String[] args) throws InterruptedException { IntLock intLock1=new IntLock(1); IntLock intLock2=new IntLock(2); Thread t1=new Thread(intLock1); t1.setName("t1"); Thread t2=new Thread(intLock2); t2.setName("t2"); t1.start(); t2.start(); Thread.sleep(1000); t2.interrupt(); } }
使用tryLock()进行限时等待。
public class TimeLock implements Runnable{ public static ReentrantLock lock=new ReentrantLock(); @Override public void run() { try{ if(lock.tryLock(5, TimeUnit.SECONDS)){ //注意返回布尔值.如果使用无参数的tryLock方法, // 当锁被占用时,线程会不等待,并返回false。 Thread.sleep(6000); }else{ System.out.println("Get lock failed!"); } }catch (Exception e){ e.printStackTrace(); }finally { if (lock.isHeldByCurrentThread()) lock.unlock(); } } public static void main(String[] args){ TimeLock timeLock=new TimeLock(); Thread t1=new Thread(timeLock); Thread t2=new Thread(timeLock); t1.start(); t2.start(); } }
公平锁:
大多数情况下,锁的申请都是不公平,随机的。而公平锁,讲究“先来后到”!因此公平锁的一大特点是不会产生饥饿。重入锁允许我们对其公平性进行设置:
public ReentrantLock(boolean fair); //true表示公平锁
公平锁缺点:性能相对低下。
二、重入锁的好基友Condition
Condition的做用类似于wait()和notify()只不过Condition和ReentrantLock配合。
Condition接口的基本方法:
void await() throws InterruptedException //释放当前线程持有的锁,进入等待状态,当其他线程使用signal或signalAll()方法时继续执行。当线程被中断时会跳出等待。 void awaitUninterruptibly();与await()方法相似,当中断时不会跳出等待。 void signal(); void signalAll();
用法示例:
public class ReentrantCondition implements Runnable{ static ReentrantLock lock=new ReentrantLock(); static Condition condition=lock.newCondition(); @Override public void run() { try{ lock.lock(); System.out.println(Thread.currentThread().getName()+" is running fast!"); condition.await(); System.out.println(Thread.currentThread().getName()+" is going no."); }catch (InterruptedException e){ e.printStackTrace(); }finally { lock.unlock(); } } public static void main(String[] args) throws InterruptedException { ReentrantCondition rc=new ReentrantCondition(); Thread t=new Thread(rc,"King of Lock"); t.start(); Thread.sleep(2000); lock.lock(); //调用signal前先要获得锁 condition.signal(); lock.unlock(); //调用singal后要释放锁,以让给被唤醒的线程。 } }
三、允许多个线程同时访问:信号量(Semaphore)
Semaphore可以指定多个线程同时访问一个资源。
public Semaphore(int permits); //permits指定同时能有几个线程访问同一资源。 public Semaphore(int permits,boolean fair) //fair指定是否采用公平锁
Semaphore主要的逻辑方法有:
public void acquire() //尝试获得一个准入许可,若无法获得,则线程会等待,直到有线程释放了一个许可,或者当前线程被中断 public void acquireUninterruptibly() //不响应中断 public void release() //释放一个许可
四、ReadWriteLock读写锁
读写锁允许多个线程同时读。
五、CountDownLatch倒计时器
通常用来控制线程等待,可以让某个线程等待直到倒计时结束,再开始执行。
public CountDownLatch(int count) //count为需要完成几个线程上的任务,CountDownLatch上的线程才能继续执行。
示例代码:
public class CountDownLatchDemo implements Runnable{ static final CountDownLatch latch=new CountDownLatch(10); //需要完成10个线程的任务, // CountDownLatch上的线程才能继续执行 static final CountDownLatchDemo demo=new CountDownLatchDemo(); @Override public void run() { try{ Thread.sleep(1000); System.out.println(Thread.currentThread().getName()+" :mission completed!"); latch.countDown();//通知CountDownLatch一个任务已经完成。 }catch (Exception e){ e.printStackTrace(); } } public static void main(String[] args) throws InterruptedException { ExecutorService ex= Executors.newFixedThreadPool(10); for(int i=0;i<10;i++){ ex.submit(demo); } latch.await(); //设置要等待其他线程完成的那个线程 System.out.println("Killed them all!"); ex.shutdown(); } }
六、循环栅栏:CyclicBarrier
与CountDownLatch类似CyclicBarrier也是用来阻止线程继续执行,让线程进入等待状态。但此计数器可以反复使用。
public CyclicBarrier(int parties,Runnable barrierAction) //barrierAction为计数完成后要执行的动作
示例代码:
public class CyclicBarrierDemo { public static class Soldier implements Runnable{ private String name; private final CyclicBarrier barrier; Soldier(String name,CyclicBarrier barrier){ this.name=name; this.barrier=barrier; } @Override public void run() { try{ barrier.await(); //进入等待状态,直到所有线程完成计数。 doWork(); //所有线程完成一次await,继续执行。 barrier.await(); //第二次await再次进入等待状态。 }catch (Exception e){ } } void doWork(){ try{ Thread.sleep(Math.abs(new Random().nextInt()%10000)); }catch (Exception e){ e.printStackTrace(); } System.out.println("任务完成!"); } } public static class FlyRun implements Runnable{ boolean flag; int count; public FlyRun(boolean flag,int count){ this.flag=flag; this.count=count; } @Override public void run() { if (flag) System.out.println("队长:"+count+" 个士兵完成任务!"); else { System.out.println("队长:"+count+" 个士兵集合完毕!"); flag=true; } } } public static void main(String[] args){ final int count=12; Thread[] soldiers=new Thread[count]; boolean flag=false; CyclicBarrier barrier=new CyclicBarrier(count,new FlyRun(flag,count)); //集合队伍 System.out.println("集合队伍!"); for(int j=0;j<count;j++){ System.out.println("士兵"+j+"报道!"); soldiers[j]=new Thread(new Soldier("士兵"+j,barrier)); soldiers[j].start(); } } }
七、线程阻塞工具类:LockSupport
LockSupport可以在线程内任意位置让线程阻塞。但与Object.wait()不同,它不需要先获得某个对象的锁,也不会抛出InterruptedException异常。示例代码:
public class LockSupportDemo { public static Object instance=new Object(); public static ChangThread t1=new ChangThread("t1"); public static ChangThread t2=new ChangThread("t2"); public static class ChangThread extends Thread{ public ChangThread(String name){ super.setName(name); } @Override public void run(){ synchronized(instance){ System.out.println("in"+getName()); LockSupport.park(); } } } public static void main(String[] args) throws InterruptedException { t1.start(); Thread.sleep(100); t2.start(); LockSupport.unpark(t1); //即使unpark()发生在park前,程序也能正常执行。 LockSupport.unpark(t2); t1.join(); t2.join(); } }
park还支持中断影响,且不抛出异常。