多线程常用工具类
countDownLatch
简介
- countDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。
- 是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。
代码
//初始化count数为2
final CountDownLatch latch=new CountDownLatch(2);
public void latchDemo(){
System.out.println("-----------主线程执行开始--------");
//创建一个线程
Thread t1=new Thread(()->{
try {
System.out.println("------t1线程执行开始-----");
TimeUnit.SECONDS.sleep(2);
System.out.println("------t1线程执行结束-----");
} catch (InterruptedException e) {
e.printStackTrace();
}
//count数减1
latch.countDown();
});
//创建第二个线程
Thread t2=new Thread(()->{
try {
System.out.println("------t2线程执行开始-----");
TimeUnit.SECONDS.sleep(2);
System.out.println("------t2线程执行结束-----");
} catch (InterruptedException e) {
e.printStackTrace();
}
//count数减1
latch.countDown();
});
//开启线程
t1.start();
t2.start();
//等待count数变为0,再继续执行主线程
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("-----------主线程执行结束--------");
}
CyclicBarrier(栅栏)
简介
- 让所有线程都等待完成后才会继续下一步行动。
例子,就像生活中我们会约朋友们到某个餐厅一起吃饭,有些朋友可能会早到,有些朋友可能会晚到,但是这个餐厅规定必须等到所有人到齐之后才会让我们进去。这里的朋友们就是各个线程,餐厅就是CyclicBarrier。
代码
private CyclicBarrier barrier;
public CyclicBarrierDemo(CyclicBarrier barrier) {
this.barrier = barrier;
}
/**
* @Description: 实例场景
* @Author: zhuyang
* @Date: 2022/1/12 18:36
* @return: void
**/
public void example(){
try {
TimeUnit.SECONDS.sleep(1);
System.out.println("线程: "+Thread.currentThread().getName()+" 到达栅栏A");
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
try {
TimeUnit.SECONDS.sleep(1);
System.out.println("线程: "+Thread.currentThread().getName()+" 到达栅栏B");
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void demo(){
//需要的线程数量
int sum=5;
CyclicBarrierDemo barrierDemo=new CyclicBarrierDemo(new CyclicBarrier(sum,()->{
System.out.println(Thread.currentThread().getName() + " 完成最后任务");
}));
//循环开启线程
for (int i = 0; i < 5; i++) {
new Thread(barrierDemo::example,"线程"+i).start();
}
//主线程开始睡眠
try {
TimeUnit.SECONDS.sleep(6);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Exchanger
简介
Exchanger 是 JDK 1.5 开始提供的一个用于两个工作线程之间交换数据的封装工具类,简单说就是一个线程在完成一定的事务后想与另一个线程交换数据,则第一个先拿出数据的线程会一直等待第二个线程,直到第二个线程拿着数据到来时才能彼此交换对应数据。
代码
private static Exchanger<String> exchanger = new Exchanger<>();
public void method(){
//开启线程1
new Thread(()->{
for (int i = 1; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"交换前:"+i);
String exchange ="";
try {
exchange =exchanger.exchange(i+"");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"交换后:"+exchange);
}
}).start();
//开启线程2
new Thread(()->{
for (;;){
String data="0";
System.out.println(Thread.currentThread().getName()+"交换前:"+data);
String exchange ="";
try {
exchange =exchanger.exchange(data);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"交换后:"+exchange);
}
}).start();
}
public void demo(){
//调用方法
method();
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
//所有程序(方法,类等)停止,系统停止运行
System.exit(-1);
}
Phaser(阶段同步器)
简介
Phaser提供了动态增减parties计数,这点比CyclicBarrier类操作parties更加方便,通过若干个方法来控制多个线程之间同步运行的结果,还可以实现针对某一个线程取消同步运行的效果,而且支持屏障处等待,在等待时还支持中断或非中断等功能,使用Java并发类对线程进行分组同步控制时,Phaser比CyclicBarrier功能更加强大,推荐使用。
- arriveAndAwaitAdvance()
当前线程已经到达屏障,在此等待一段时间,等条件满足后继续向下一个屏障继续执行。 - arriveAndDeregister()
当前线程退出,并且使parties值减1。
代码
//产生一个随机数
public static Random random=new Random();
static MarriagePhaser phaser = new MarriagePhaser();
public static void milliSleep(int m){
try {
TimeUnit.MILLISECONDS.sleep(m);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void demo(){
//设置阻塞队列个数
phaser.bulkRegister(5);
//进行开启线程
for (int i = 0; i < 5; i++) {
int nameIndex=i;
new Thread(()->{
//创建用户
Person person=new Person("person"+nameIndex);
//安排动作
person.arrive();
//进行阻塞
phaser.arriveAndAwaitAdvance();
//安排动作
person.eat();
//进行阻塞
phaser.arriveAndAwaitAdvance();
//安排动作
person.leave();
//进行阻塞
phaser.arriveAndAwaitAdvance();
}).start();
}
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static class MarriagePhaser extends Phaser{
@Override
protected boolean onAdvance(int phase, int registeredParties) {
switch (phase){
case 0:
System.out.println("所有人到齐了");
return false;
case 1:
System.out.println("所有人吃完了");
return false;
case 2:
System.out.println("所有人离开了");
System.out.println("婚礼结束!");
return true;
default:
return true;
}
}
}
/**
* @Description: 创建实体
* @Author: zhuyang
* @Date: 2022/1/13 9:43
* @return: null
**/
public static class Person{
public String name;
public Person(String name) {
this.name = name;
}
public void arrive(){
milliSleep(random.nextInt(1000));
System.out.printf("%s 到达现场\n",name);
}
public void eat(){
milliSleep(random.nextInt(1000));
System.out.printf("%s 吃完了\n",name);
}
public void leave(){
milliSleep(random.nextInt(1000));
System.out.printf("%s 离开了\n",name);
}
}
ReentrantLock(公平锁)
简介
ReentrantLock是独占锁,加锁和解锁的过程需要手动进行,可以响应中断,可以实现公平锁机制。
代码
//创建一个公平锁 默认为非公平锁
ReentrantLock lock=new ReentrantLock(true);
public void reentrant(){
System.out.println("当前线程名为----->"+Thread.currentThread().getName());
System.out.println("--------------------------------------------------------------------");
lock.lock();
try {
TimeUnit.SECONDS.sleep(1);
System.out.println("当前线程名为: "+Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void demo(){
ReentrantLockDemo lockDemo=new ReentrantLockDemo();
new Thread(lockDemo::reentrant, "t1").start();
new Thread(lockDemo::reentrant, "t2").start();
new Thread(lockDemo::reentrant, "t3").start();
new Thread(lockDemo::reentrant, "t4").start();
new Thread(lockDemo::reentrant, "t5").start();
try {
TimeUnit.SECONDS.sleep(6);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Semaphore(信号量(n个线程的限流))
简介
维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问资源的线程个数,实现一个文件允许的并发访问数。
代码
//初始化
Semaphore semaphore=new Semaphore(2);
public void createThread(){
Thread t1=new Thread(()->{
//获取一个许可
try {
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前线程名:"+Thread.currentThread().getName()+" 开始运行");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前线程名:"+Thread.currentThread().getName()+" 运行结束");
semaphore.release();
});
Thread t2=new Thread(()->{
try {
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前线程名:"+Thread.currentThread().getName()+" 开始运行");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前线程名:"+Thread.currentThread().getName()+" 运行结束");
semaphore.release();
});
t1.start();
t2.start();
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
ThreadLocal
简介
ThreadLocal是JDK包提供的,它提供线程本地变量,如果创建一乐ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个副本,在实际多线程操作的时候,操作的是自己本地内存中的变量,从而规避了线程安全问题。
原理
往ThreadLocal里面设置值时,其实是往ThreadLocalMap里面设置值,key值为当前线程的hash值,value为当前设置的对象值,取也同理,用当前线程作为key,取其value。
代码
//创建ThreadLocal对象
ThreadLocal<Person> threadLocal=new ThreadLocal<>();
Person person=new Person();
public void threadLocal(){
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(2);
threadLocal.set(new Person());
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"输出对象name值为: "+threadLocal.get());
},"t1").start();
new Thread(()->{
//更改Person中的name值
threadLocal.set(new Person());
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"输出对象name值为: "+threadLocal.get());
},"t2").start();
}
public void ordinary(){
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"输出对象name值为: "+person.name);
},"t3").start();
new Thread(()->{
//更改Person中的name值
person.name="lisi";
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"输出对象name值为: "+person.name);
},"t4").start();
}
/**
* t1输出对象name值为: null
* t3输出对象name值为: lisi
* t2输出对象name值为: com.yxkj.juc.c_000.ThreadLocalDemo$Person@3d8f1de9
* t4输出对象name值为: lisi
**/
public void demo(){
threadLocal();
ordinary();
}
public static class Person{
public String name="zhangsan";
}
Gitee地址
XFS