Java高并发程序设计笔记6之JDK同步控制

时间:2021-04-15 23:49:27

CountDownLatch

倒数计时器一种典型的场景就是火箭发射。在火箭发射前,为了保证万无一失,往往还要进行各项设备、仪器的检查。 只有等所有检查完毕后,引擎才能点火。这种场景就非常适合使用CountDownLatch。它可以使得点火线程,等待所有检查线程全部完工后,再执行
主要接口 
static final CountDownLatch end = new CountDownLatch(10); 
end.countDown(); 
end.await();
Java高并发程序设计笔记6之JDK同步控制
一个简单的例子:
package com.cosmistar.jk;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
* Created by Administrator on 2016/11/20.
*/
public class CountDownLatchDemo implements Runnable{
static final CountDownLatch countDownLatch = new CountDownLatch(10);
static final CountDownLatchDemo t = new CountDownLatchDemo();
@Override
public void run() {
try{
Thread.sleep(2000);
System.out.println("complete");
countDownLatch.countDown();
}catch (Exception e) {
e.printStackTrace();
}
}

public static void main(String[] args) throws InterruptedException
{
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
executorService.execute(t);
}
countDownLatch.await();
System.out.println("end");
executorService.shutdown();
}
}
主线程必须等待10个线程全部执行完才会输出"end"

CyclicBarrier

循环栅栏 Cyclic意为循环,也就是说这个计数器可以反复使用。比如,假设我们将计数器设置为10。那么凑齐第一批10个线程后,计数器就会归零,然后接着凑齐下一批10个线程
主要接口
//barrierAction就是当计数器一次计数完成后,系统会执行的动作
public CyclicBarrier(int parties, Runnable barrierAction) 
await()
Java高并发程序设计笔记6之JDK同步控制
下面举个例子:
package com.cosmistar.jk;

import java.util.concurrent.CyclicBarrier;

/**
* Created by Administrator on 2016/11/20.
*/
public class CyclicBarrierDemo implements Runnable{
private String soldier;
private final CyclicBarrier cyclic;

public CyclicBarrierDemo(String soldier, CyclicBarrier cyclic) {
this.soldier = soldier;
this.cyclic = cyclic;
}

@Override
public void run() {
try {
//等待所有士兵到齐
cyclic.await();
dowork();
//等待所有士兵完成工作
cyclic.await();
}catch (Exception e){
// TODO Auto-generated catch block
e.printStackTrace();
}

}

private void dowork()
{
// TODO Auto-generated method stub
try {
Thread.sleep(3000);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println(soldier + ": done");
}

public static class BarrierRun implements Runnable
{

boolean flag;
int n;

public BarrierRun(boolean flag, int n)
{
super();
this.flag = flag;
this.n = n;
}

@Override
public void run()
{
if (flag)
{
System.out.println(n + "个任务完成");
}
else
{
System.out.println(n + "个集合完成");
flag = true;
}

}
}

public static void main(String[] args)
{
final int n = 10;
Thread[] threads = new Thread[n];
boolean flag = false;
CyclicBarrier barrier = new CyclicBarrier(n, new BarrierRun(flag, n));
System.out.println("集合");
for (int i = 0; i < n; i++)
{
System.out.println(i + "报道");
threads[i] = new Thread(new CyclicBarrierDemo("士兵" + i, barrier));
threads[i].start();
}
}
}

LockSupport

提供线程阻塞原语 
主要接口 
LockSupport.park(); 
LockSupport.unpark(t1);
与suspend()比较 不容易引起线程冻结
中断响应:能够响应中断,但不抛出异常 
中断响应的结果是,park()函数的返回,可以从Thread.interrupted()得到中断标志
LockSupport的思想呢,和 Semaphore有点相似,内部有一个许可,park的时候拿掉这个许可,unpark的时候申请这个许可。所以如果unpark在park之前,是不会发生线程冻结的。
下面的代码是多线程基础中suspend示例代码,在使用suspend时会发生死锁。
package com.cosmistar.jk;

import java.util.concurrent.locks.LockSupport;

/**
* Created by Administrator on 2016/11/20.
*/
public class LockSupportDemo {
static Object u = new Object();
static TestSuspendThread t1 = new TestSuspendThread("t1");
static TestSuspendThread t2 = new TestSuspendThread("t2");

public static class TestSuspendThread extends Thread
{
public TestSuspendThread(String name)
{
setName(name);
}

@Override
public void run()
{
synchronized (u)
{
System.out.println("in " + getName());
Thread.currentThread().suspend();
//LockSupport.park();
}
}
}

public static void main(String[] args) throws InterruptedException
{
t1.start();
Thread.sleep(100);
t2.start();
t1.resume();
t2.resume();
//LockSupport.unpark(t1);
//LockSupport.unpark(t2);
t1.join();
t2.join();
}
}
而使用 LockSupport则不会发生死锁。
另外park()能够响应中断,但不抛出异常。中断响应的结果是,park()函数的返回,可以从Thread.interrupted()得到中断标志。
在JDK当中有大量地方使用到了park,当然LockSupport的实现也是使用unsafe.park()来实现的。
public static void park() {
unsafe.park(false, 0L);
}

ReentrantLock 的实现

下面来介绍下ReentrantLock的实现,ReentrantLock的实现主要由3部分组成:
CAS状态
等待队列
.park()
//ReentrantLock的父类中会有一个state变量来表示同步的状态
** * The synchronization state. */ private volatile int state;//通过CAS操作来设置state来获取锁,如果设置成了1,则将锁的持有者给当前线程final void lock() {  if (compareAndSetState(0, 1))  setExclusiveOwnerThread(Thread.currentThread());  else  acquire(1); }//如果拿锁不成功,则会做一个申请public final void acquire(int arg) { if (!tryAcquire(arg) &&  acquireQueued(addWaiter(Node.EXCLUSIVE), arg))  selfInterrupt(); }//首先,再去申请下试试看tryAcquire,因为此时可能另一个线程已经释放了锁。//如果还是没有申请到锁,就addWaiter,意思是把自己加到等待队列中去private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode); // Try the fast path of enq; backup to full enq on failure Node pred = tail; if (pred != null) {  node.prev = pred;  if (compareAndSetTail(pred, node)) {  pred.next = node;  return node;  } } enq(node); return node; }//其间还会有多次尝试去申请锁,如果还是申请不到,就会被挂起private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); }//同理,如果在unlock操作中,就是释放了锁,然后unpark,这里就不具体讲了。