JUC是什么
juc是java.util.concurrent包的缩写。
JUC包括什么
那么我们简要概括一下JUC的内容包括哪些呢?
1.LOCK框架、TOOLS类
2.Collections 集合。
3.Atomic 原子类。
4.Executors 线程池
1.TOOlS类解析
CountDownLatch
俗称闭锁。类似加强版的Join。作用是:让一组线程等待其他线程完成工作之后在进行工作。
举一个例子:Springboot启动类在启动的时候,主线程是需要环境线程初始化完之后才开始的。按照我的理解就是启动服务框架的时候,控制台开始打印哪些信息,但是要是其中你一个环境变量出了问题。按照经验启动服务框架就失败了。所以主线程是等待环境线程初始化完之后才开始工作的。这个过程可以利用CountDownLatch实现。
如何使用:(以下内容借鉴网上大佬博客)
1.首先在使用CountDownLatch的时候需要传入一个int值用于初始化。然后这个int扣减完之后,等待的线程就被唤醒了。如以下代码
/**
* Constructs a {@code CountDownLatch} initialized with the given count.
*
* @param count the number of times {@link #countDown} must be invoked
* before threads can pass through {@link #await}
* @throws IllegalArgumentException if {@code count} is negative
*/
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
观察以下图
1.CountDownLatch(int)初始化方法
2.await()等待方法,带参数的是超时重载方法。
3.countDown()对初始化的int参数进行减1的操作。
4.getCount()获取当前值。
5.sync的话是一个内部类,以上所介绍的方法都是为了这个服务的。
按照网上大佬的代码复现了一下。
package JUC.TOOLS.CountDonwLatch;
import java.util.concurrent.TimeUnit;
public class SleepTools {
public static final void second(int second){
try {
TimeUnit.SECONDS.sleep(second);
}catch (InterruptedException e){
e.printStackTrace();
}
}
public static final void ms(int ms){
try{
TimeUnit.MILLISECONDS.sleep(ms);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
package JUC.TOOLS.CountDonwLatch;
import java.util.concurrent.CountDownLatch;
public class UseCountDownLatch {
static CountDownLatch countDownLatch = new CountDownLatch(6);
/**
* 初始化线程
*/
private static class InitThread implements Runnable {
@Override
public void run() {
System.out.println("初始化thread_" + Thread.currentThread().getId() + "ready init work ...");
countDownLatch.countDown();
for (int i = 0; i < 2; i++) {
System.out.println("循环初始化thread_" + Thread.currentThread().getId() + ".....continue do its work");
}
}
}
/**
* 业务线程:等初始化线程初始化之后再执行。
*/
private static class BusiThread implements Runnable {
@Override
public void run() {
try {
countDownLatch.await();
for (int i = 0; i < 3; i++) {
System.out.println("蔡拯雨BusiThread " + Thread.currentThread().getId() + " do busiThread------");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new Thread() {
@Override
public void run() {
SleepTools.ms(1);
System.out.println("thread_" + Thread.currentThread().getId() + " ready init work step 1st.....");
countDownLatch.countDown();
System.out.println("begin stop 2nd.....");
SleepTools.ms(1);
System.out.println("thread_" + Thread.currentThread().getId() + " ready init work step 2nd.....");
countDownLatch.countDown();
}
}.start();
new Thread(new BusiThread()).start();
for (int i=0 ;i<=3;i++){
new Thread(new InitThread()).start();
}
try{
countDownLatch.await();
System.out.println("Main do ites work.....");
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
效果如图
能够很清楚的看到,虽然我业务线程先启动,但是在CountDownLatch的干涉下,其实业务线程还是在初始化线程结束之后才开始工作的。
CyclicBarrier
江湖俗称栅栏锁。意思是作用于一组线程。只有这一组线程里面的所有线程都到达栅栏锁的面前,这一组线程才能继续通行,少一个也不行。举一个例子:其实好比你在放羊,当所有的羊到宿舍门口的时候,宿舍门才能打开。少一只所有羊都不能进去睡觉。
我们来看一下他的两个构造函数。
很明显能看出是第一个调用第二个。那么我们来看一下它的参数:parties是你这一组线程的数量,不能多不能少,必须是刚刚好等于你这一组线程的数量。barrierAction是一个线程引用。这个线程的作用是,当你之前设置的一组线程全部达到这个屏障的时候,屏障打开,barrierAction开始工作。
/**
* Creates a new {@code CyclicBarrier} that will trip when the
* given number of parties (threads) are waiting upon it, and
* does not perform a predefined action when the barrier is tripped.
*
* @param parties the number of threads that must invoke {@link #await}
* before the barrier is tripped
* @throws IllegalArgumentException if {@code parties} is less than 1
*/
public CyclicBarrier(int parties) {
this(parties, null);
}
/**
* Creates a new {@code CyclicBarrier} that will trip when the
* given number of parties (threads) are waiting upon it, and which
* will execute the given barrier action when the barrier is tripped,
* performed by the last thread entering the barrier.
*
* @param parties the number of threads that must invoke {@link #await}
* before the barrier is tripped
* @param barrierAction the command to execute when the barrier is
* tripped, or {@code null} if there is no action
* @throws IllegalArgumentException if {@code parties} is less than 1
*/
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
实践一下。
package JUC.TOOLS.CyclicBarrier;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class UseCyclicBarrier {
static CyclicBarrier cyclicBarrier = new CyclicBarrier(5,new SoutThread());
private static class WorkThread implements Runnable{
@Override
public void run() {
try{
System.out.println("蔡拯雨thread_"+Thread.currentThread().getId()+"wait");
cyclicBarrier.await();
//Thread.sleep(2000);
System.out.println("蔡拯雨thread_"+Thread.currentThread().getId()+"work");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
private static class SoutThread implements Runnable{
@Override
public void run() {
System.out.println("开始开始开始");
}
}
public static void main(String[] args) {
for (int i=0;i<=4;i++){
Thread thread = new Thread(new WorkThread());
thread.start();
}
}
}
看一下效果:五个线程都等待了(到达屏障),初始化设置的barrierAction线程开始工作。
Semaphore
江湖俗称信号量。和操作系统中的功能一致,都是用来控制对某一资源访问的数量。比如控制数据库的连接数量。
一般适用semaphore的时候要设置两个。为什么呢?想想操作系统里面针对信号量的pv操作。
看一下构造函数。
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
/**
* Creates a {@code Semaphore} with the given number of
* permits and the given fairness setting.
*
* @param permits the initial number of permits available.
* This value may be negative, in which case releases
* must occur before any acquires will be granted.
* @param fair {@code true} if this semaphore will guarantee
* first-in first-out granting of permits under contention,
* else {@code false}
*/
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
没啥区别,只是默认的是非公平锁实现,也可以指定公平锁实现。
利用这个我们来实践一下。
package JUC.TOOLS.Semaphore;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Semaphore;
public class UseSemaphore {
static Semaphore s1,s2;
//连接池
static LinkedList<Integer> Pool = new LinkedList<>();
//初始化连接池
static{
s1 = new Semaphore(10);
s2 = new Semaphore(0);
for (int i=0;i<10;i++){
Pool.add(1);
}
}
//连接数据库资源
public void connect() throws InterruptedException {
s1.acquire();
synchronized (Pool){
System.out.println(Pool.removeFirst());
}
s2.release();
}
//释放连接
public void releaseConnect() throws InterruptedException {
s2.acquire();
synchronized (Pool){
System.out.println(Pool.add(1));
}
s1.release();
}
}
Exchanger
交换器,可以交换两个线程之间的数据。下面试一下三个线程会发生什么。
package JUC.TOOLS.Exchanger;
import com.sun.org.slf4j.internal.Logger;
import com.sun.org.slf4j.internal.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Exchanger;
public class UseExchanger {
private static Exchanger<List<Integer>> exchanger = new Exchanger<>();
Logger logger = LoggerFactory.getLogger(UseExchanger.class);
public static void main(String[] args) {
new Thread(){
@Override
public void run(){
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
try {
List<Integer> list1 = exchanger.exchange(list);
for (int i=0 ;i<list1.size();i++){
System.out.println("111:"+list1.get(i));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
new Thread(){
@Override
public void run(){
List<Integer> list = new ArrayList<>();
list.add(3);
list.add(4);
try {
List<Integer> list1 = exchanger.exchange(list);
for (int i=0 ;i<list1.size();i++){
System.out.println("222:"+list1.get(i));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
// new Thread(){
// @Override
// public void run(){
// List<Integer> list = new ArrayList<>();
// list.add(5);
// list.add(6);
// try {
// List<Integer> list1 = exchanger.exchange(list);
// for (int i=0 ;i<list1.size();i++){
// System.out.println("333:"+list1.get(i));
// }
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//
// }
// }.start();
}
}
这个是两个线程的效果。但是你加入第三个之后会发现程序不会报错,但是线程无法结束。这是为什么有大佬知道请留言谢谢