JAVA同步工具类之栅栏、信号量、闭锁

1. 栅栏

栅栏类似于闭锁,它能阻塞一组线程直到某个事件发生,CyclicBarrier可以使一定数量的参与方反复的在栅栏处汇集。

当线程到达栅栏处,将调用CyclicBarrier.await方法,这个方法一直阻塞直到所有线程到达栅栏处,当所有线程到达了栅栏处,栅栏将打开,所有线程被释放,而栅栏被重置以便下次使用。

栅栏与闭锁区别
所有线程必须全部到达栅栏处,才能继续执行;闭锁结束前,不允许线程执行,结束时,允许所有线程执行
栅栏等待线程;闭锁等待事件

/**
 * 栅栏理解代码示例
 */
public class CyclicBarrierWorker {

    class Worker implements Runnable {

        private int id;
        private CyclicBarrier cyclicBarrier;

        public Worker(int id, CyclicBarrier cyclicBarrier) {
            this.id = id;
            this.cyclicBarrier = cyclicBarrier;
        }

        @Override
        public void run() {
            System.out.println("线程" + id + "已到达栅栏处");
            try {
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(10, () -> {
            System.out.println("所有线程已到达栅栏处, 2333");
        });

        for (int i = 0; i < 10; i++) {
            new Thread(new CyclicBarrierWorker().new Worker(i, cyclicBarrier)).start();
        }
    }
}

运行结果

线程0已到达栅栏处
线程4已到达栅栏处
线程3已到达栅栏处
线程2已到达栅栏处
线程1已到达栅栏处
线程6已到达栅栏处
线程8已到达栅栏处
线程7已到达栅栏处
线程5已到达栅栏处
线程9已到达栅栏处
所有线程已到达栅栏处, 2333

2. 信号量

计数信号量(Counting Semaphore)用来控制同时访问某个资源的操作数量。信号量还可以实现资源池,为容器设置边界。

Semaphore管理着一组虚拟的许可,许可的初始数量可以通过构造函数来指定。acquire获取许可,没有许可则阻塞直到有许可,release将返回一个许可给信号量。

/**
 * 使用Semaphore为容器设置边界
 */
public class BoundedHashSet<T> {

    private final Set<T> set;
    private final Semaphore semaphore;

    public BoundedHashSet(int bound) {
        set = Collections.synchronizedSet(new HashSet<>());
        semaphore = new Semaphore(bound);
    }

    public boolean add(T e) throws InterruptedException {
        semaphore.acquire();
        boolean wasAdded = false;
        try {
            wasAdded = set.add(e);
            return wasAdded;
        } finally {
            if (!wasAdded) {
                semaphore.release();
            }
        }
    }

    public boolean remove(Object o) {
        boolean wasRemoved = set.remove(o);
        if (wasRemoved) {
            semaphore.release();
        }
        return wasRemoved;
    }
}

3. 闭锁

闭锁是一种同步工具类,可以延迟线程的进度直到其到达终止状态。
闭锁相当于一扇门,在闭锁结束之前,这扇门一直关闭,没有任何线程能够通过;当闭锁结束时,这扇门打开,所有的线程均可通过

闭锁状态包括一个计数器,该计数器初始化为一个正数,表示需要等待的事件数量。countDown方法递减计数器,表示一个事件已经发生,如果计数器非零,则await方法等待计数器为零,或等待中的线程中断或者等待超时。

闭锁是一次性对象,一旦进入终止状态,就不能被重置。

import java.util.concurrent.CountDownLatch;

/**
 * 闭锁实现统计多个线程执行时间
 */
public class TestHarness {

   public long timeTasks(int nThreads, final Runnable task) throws InterruptedException {
       CountDownLatch startGate = new CountDownLatch(1);
       CountDownLatch endGate = new CountDownLatch(nThreads);

       for (int i = 0 ; i < nThreads; i++) {
           new Thread(() -> {
               try {
                   startGate.await();
                   try {
                       task.run();
                   } finally {
                       endGate.countDown();
                   }
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }).start();
       }

       long startTime = System.nanoTime();
       startGate.countDown();
       endGate.await();
       long endTime = System.nanoTime();
       return endTime - startTime;
   }

    public static void main(String[] args) throws InterruptedException {
       Runnable runnable = () -> {
           for (int i = 0; i < 10000; i++) {

           }
       };
        long time = new TestHarness().timeTasks(4, runnable);
        System.out.println(time);
    }
}

4. JAVA程序启动至少会启动几个线程

ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
for (ThreadInfo threadInfo : threadInfos) {
    System.out.println(threadInfo.getThreadId() + ":" + threadInfo.getThreadName());
}

JDK1.8 执行结果:

	6:Monitor Ctrl-Break
	5:Attach Listener 接收外部JVM命令
	4:Signal Dispatcher 分发到不同的模块进行处理外部JVM命令,并且返回处理结结果
	3:Finalizer 调用对象的finalize方法的线程,即垃圾回收的线程
	2:Reference Handler 清除reference的线程
	1:main 主线程

查看以上线程所属线程组及优先级

public class Test {

    public static void main(String[] args) {
        Thread threadOne = new Thread(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        threadOne.setName("threadOne");
        threadOne.start();

        ThreadGroup threadGroup = threadOne.getThreadGroup();
        System.out.println(threadGroup);

        ThreadGroup systemThreadGroup = threadGroup.getParent();
        System.out.println(systemThreadGroup);

        systemThreadGroup.list();
    }
}

执行结果:

java.lang.ThreadGroup[name=main,maxpri=10]
java.lang.ThreadGroup[name=system,maxpri=10]
java.lang.ThreadGroup[name=system,maxpri=10]
    Thread[Reference Handler,10,system]
    Thread[Finalizer,8,system]
    Thread[Signal Dispatcher,9,system]
    Thread[Attach Listener,5,system]
    java.lang.ThreadGroup[name=main,maxpri=10]
        Thread[main,5,main]
        Thread[Monitor Ctrl-Break,5,main]
        Thread[threadOne,5,main]

由此看来,当一个应用程序启动时,至少创建6个线程,两个线程组(system和main线程组),其中system线程组是main线程组的父线程组,main与Monitor Ctrl-Break处于main线程组,其他线程处于system线程组。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

算法小生Đ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值