一、ThreadLocal

ThreadLocal类用于隔离多线程中使用的对象,为ThreadLocal类中传递的泛型就是要隔离的对象,简单的来说:如果我们在主线程创建了一个对象,并且需要给下面的多线程任务都传递这个对象,那么如果这个对象传递到ThreadLocal,那么每个线程获取的对象都是独立的,不会受其他线程的改变而改变。

  ThreadLocal中一共有三个常用方法:

  get()方法:获取与当前线程关联的ThreadLocal值。

  set(T value)方法:设置与当前线程关联的ThreadLocal值。

  initialValue()方法:设置与当前线程关联的ThreadLocal初始值。

我们来看一个列子,创建两个线程,两个线程共同使用一个对象,我们来观察这个对象的值以及ThreadLocal中这个对象的值:

  对象User类:

publicclassUser{int num;publicUser(int num){this.num = num;}/*** 我们只使用get方法,并且每次获取num都为其加1 * * 使用synchronized保证getNum获取的num是线程安全的 * @return */synchronizedpublicint getNum(){return num++;}}

  线程任务类:

publicclassThreadlocaDemoextendsThread{User user;ThreadlocaDemo(User user){this.user=user;}/** * 创建一个ThreadLocal对象,为它的泛型传入User */ThreadLocal<User> userLoacl =newThreadLocal<User>(){/** * 初始化方法,将设置user中的初始值*/@OverrideprotectedUser initialValue(){User user=newUser(10);return user;}};@Overridepublicvoid run(){for(int i =1; i <3; i++){//休息一会儿try{Thread.sleep(100);}catch(InterruptedException e){ e.printStackTrace();}//打印user中获取的值System.out.println(getName()+“user对象的值”+user.getNum());//打印ThreadLocal中的user对象的值System.out.println(getName()+“ThreadLocal中的user对象的值”+userLoacl.get().getNum());}}}

  测试类:

public class Main { public static void main(String[] args) {

//创建一个共用的对象

User user=new User(10);

//创建两个线程任务

ThreadlocaDemo threadlocaDemo1=new ThreadlocaDemo(user);

ThreadlocaDemo threadlocaDemo2=new ThreadlocaDemo(user);

threadlocaDemo1.setName(“这是1号线程:”);

threadlocaDemo2.setName(“这是2号线程:”); threadlocaDemo1.start();

threadlocaDemo2.start();

}

}

运行的结果:

java并发学习–第七章 JDK提供的线程工具类插图

根据运行结果我们可以清楚的看到,在ThreadLocal中的user对象是隔离的,外面的user对象没有被隔离,被两个线程都进行修改过。

二、Exchanger

Exchanger可以交换两个线程的数据,它的实现思想是当线程进行到Exchanger类调用的exchange方法时,会阻塞当前线程,直到有其他线程也进入了exchange方法中就开始交换两个线程的数据。

  需要注意的是,exchange只能作为两个线程进行交换数据,如果有多个线程,那么获取的数据是随机的。

  我们来看一个列子:

  线程任务类,交换一个字符串数据

public class ExchangerThread extends Thread{ //Exchanger对象

Exchanger change;

//当前线程中的数据,需要交换的数据

String thisStr; public ExchangerThread(Exchanger change, String thisStr) {

this.change = change;

this.thisStr = thisStr;

} @Override

public void run() {

try {

//exchange方法,将要交换的数据传递到exchange方法中

//获取的值就是交换后的数据

String getStr=change.exchange(thisStr);

System.out.println(getName()+getStr);

} catch (InterruptedException e) {

e.printStackTrace();

} }

}

  测试类:

public class ExchangerDemo { public static void main(String[] args) {

//创建Exchanger对象

Exchanger exchanger=new Exchanger<>();

//创建两个线程交换数据

ExchangerThread exchangerThread1=new ExchangerThread(exchanger,”这是1号线程的数据”);

ExchangerThread exchangerThread2=new ExchangerThread(exchanger,”这是2号线程的数据”);

exchangerThread1.setName(“我是1号线程,交互获取的数据是:”);

exchangerThread2.setName(“我是2号线程,交互获取的数据是:”);

exchangerThread1.start();

exchangerThread2.start();

}

}

运行的结果:

java并发学习–第七章 JDK提供的线程工具类插图1

可以看到如果只有两个线程,交换的数据是确定的,如果有多个线程呢,我们在测试类中多添加几个线程:

public class ExchangerDemo { public static void main(String[] args) {

//创建Exchanger对象

Exchanger exchanger=new Exchanger<>();

//创建两个线程交换数据

ExchangerThread exchangerThread1=new ExchangerThread(exchanger,”这是1号线程的数据”);

ExchangerThread exchangerThread2=new ExchangerThread(exchanger,”这是2号线程的数据”);

ExchangerThread exchangerThread3=new ExchangerThread(exchanger,”这是3号线程的数据”);

ExchangerThread exchangerThread4=new ExchangerThread(exchanger,”这是4号线程的数据”);

exchangerThread1.setName(“我是1号线程,交互获取的数据是:”);

exchangerThread2.setName(“我是2号线程,交互获取的数据是:”);

exchangerThread3.setName(“我是3号线程,交互获取的数据是:”);

exchangerThread4.setName(“我是4号线程,交互获取的数据是:”);

exchangerThread1.start();

exchangerThread2.start();

exchangerThread3.start();

exchangerThread4.start();

}}

运行的结果:

java并发学习–第七章 JDK提供的线程工具类插图2

可以看到,多个线程进行交换数据,数据的交换是随机的。

三、CountDownLatch

CountDownLatch是一个同步辅助类,在完成一组正在其他线程中执行的操作前,它允许一个或多个线程一直等待。简单的说就是一个线程组正在工作,但是他们执行的任务都有一道门,这道门是关着的,会导致每个线程接下来的任务都暂停进行,简单的说就是线程被阻塞了,只有当所有线程都在这个门前,即所有线程都被阻塞了,才会继续执行。

当然了,上面的例子不太准确,CountDownLatch中实现所有线程都完成任务才能继续进行的方式不是这样,它是有一个计算器,当这个计算器的值等于0时,才会释放当前阻塞的所有线程。

  CountDownLatch中常用的方法:

  getCount():获取当前计数器剩余计数

  countDown():计算器的值减1

await():阻塞当前线程,只有当计算器的值等于0时,才会释放当前线程

 例子:

 创建一个CountDownLatch对象,它的计数器的值为3,我们创建3个线程,每个线程执行完任务后先countDown(),再调用await()方法等待:

 线程任务类:

public class CountDownLatchDemo extends Thread { // 创建CountDownLatch对象

CountDownLatch latch;

// 休眠时间

int num; // 构造器传参

public CountDownLatchDemo(CountDownLatch latch, int num) {

super();

this.latch = latch;

this.num = num;

} @Override

public void run() {

try {

System.out.println(“当前执行的是:” + getName());

// 让线程执行任务

Thread.sleep(num);

// 线程执行完成任务后,对CountDownLatch的值减1

latch.countDown();

System.out.println(getName() + “完成任务,等待其他线程执行任务”);

// 这里让线程阻塞,只有当CountDownLatch等于0时才继续执行线程

// 即当所有的线程都完成了任务,才开始一起继续执行下面的任务

latch.await();

System.out.println(“所有线程都完成后才执行的代码:” + getName());

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} }}

  测试类:

public class Main { public static void main(String[] args) {

//创建一个CountDownLatch对象,设置值为3,表示当有3个线程都完成任务后,才开始执行阻塞后的代码

CountDownLatch latch=new CountDownLatch(3) ;

//创建3个线程,每个线程的CountDownLatch对象是同一个

CountDownLatchDemo countDownLatchDemo1=new CountDownLatchDemo(latch,100);

CountDownLatchDemo countDownLatchDemo2=new CountDownLatchDemo(latch,1000);

CountDownLatchDemo countDownLatchDemo3=new CountDownLatchDemo(latch,2000); countDownLatchDemo1.setName(“1号线程”);

countDownLatchDemo2.setName(“2号线程”);

countDownLatchDemo3.setName(“3号线程”); countDownLatchDemo1.start();

countDownLatchDemo2.start();

countDownLatchDemo3.start();

}}

  运行的结果:

java并发学习–第七章 JDK提供的线程工具类插图3

根据运行的结果我们可以看到,1、2、3号线程在执行完成任务后都等待,等待3号执行完成任务,使得计算器的值为0时,开始释放所有线程。

四、CyclicBarrier、

CyclicBarrier与countDownLatch相识,都有一个计算器,但是两个类不同的是:countDownLatch都是当计算器的值为0时才释放所有线程,而CyclicBarrier是当阻塞的线程数等于计算器的值时,才开始释放线程。这两个类最大的区别就是一个是线程开始时的阻塞,一个是线程结束前的阻塞。

CyclicBarrier可以使一定数量的线程反复地在栅栏位置处汇集。当线程到达栅栏位置时将调用await方法,这个方法将阻塞直到所有线程都到达栅栏位置。如果所有线程都到达栅栏位置,那么栅栏将打开,此时所有的线程都将被释放,而栅栏将被重置以便下次使用。

  我们来看一个例子:阻塞了三个线程后,开始执行:

  线程任务类: 

public class CyclicBarrierDemo extends Thread{ //创建CyclicBarrier对象

CyclicBarrier cyclicBarrier;

//休眠时间

int num; public CyclicBarrierDemo(CyclicBarrier cyclicBarrier, int num) {

super();

this.cyclicBarrier = cyclicBarrier;

this.num = num;

} @Override

public void run() {

try {

System.out.println(“任务开始,当前执行的是:”+getName());

//让线程执行任务

Thread.sleep(num);

System.out.println(getName()+”准备工作完成,等待其他线程执行任务”);

//执行完成后,等待其他线程完成任务

cyclicBarrier.await();

Thread.sleep(num);

System.out.println(“所有线程都准备好后才执行的代码:我是”+getName());

} catch (Exception e) {

e.printStackTrace();

}

}}

  测试类:

public static void main(String[] args) {

//阻塞的线程数量

int waitNum=3;

//创建cyclicBarrier对象

CyclicBarrier cyclicBarrier=new CyclicBarrier(waitNum); CyclicBarrierDemo cyclicBarrierDemo1=new CyclicBarrierDemo(cyclicBarrier,100);

CyclicBarrierDemo cyclicBarrierDemo2=new CyclicBarrierDemo(cyclicBarrier,1000);

CyclicBarrierDemo cyclicBarrierDemo3=new CyclicBarrierDemo(cyclicBarrier,2000); cyclicBarrierDemo1.setName(“1号线程:”);

cyclicBarrierDemo2.setName(“2号线程:”);

cyclicBarrierDemo3.setName(“3号线程:”); cyclicBarrierDemo1.start();

cyclicBarrierDemo2.start();

cyclicBarrierDemo3.start(); }}

运行的结果:

  java并发学习–第七章 JDK提供的线程工具类插图4

五、Semaphore

semaphore的作用是控制资源的访问,他能够限制当前资源能够被多少个线程所访问。它和线程池有些类似,但是与线程池不同的是,线程池是提前生成好了多个线程放进线程池里,如果超出线程池设置的数量,那么线程是不会被创建,但是semaphore超出了设置的线程数,还会继续创建线程。

  semaphore中的方法:

  acquire():  获取许可

  release():  释放资源

  availablePermits():  获取当前可用的资源数量

  semaphore的构造方法

public Semaphore(int permits,boolean fair)

  permits:初始化设置线程的数据

    fair:表示是否以线程获取锁的顺序来执行线程,就是是否用公平锁,false表示不使用,默认是false

  我们来看一个例子,创建1个线程数组和一个semaphore任务类,线程数组的长度为10,semaphore任务类的值为5,我们观察它们的执行方式:

semaphore任务类:

public class SemaphoreDemo extends Thread { Semaphore semaphore; public SemaphoreDemo(Semaphore semaphore) {

super();

this.semaphore = semaphore;

} @Override

public void run() {

try {

// 获取许可

semaphore.acquire();

System.out.println(“任务开始,当前执行的是:” + getName());

// 让线程执行任务

Thread.sleep(2000);

// 执行完成后,释放资源

semaphore.release();

} catch (Exception e) {

e.printStackTrace();

} }}

  测试类:

public class Main { public static void main(String[] args) {

// 创建Semaphore对象

Semaphore semaphore = new Semaphore(5);

// 创建一个长度为10的线程数组

Thread[] threadAry = new Thread[10];

for (int i = 0; i < threadAry.length; i++) {

// 线程数组的值都添加线程任务

SemaphoreDemo semaphoreDemo = new SemaphoreDemo(semaphore);

semaphoreDemo.setName(i + “号线程”);

threadAry[i] = semaphoreDemo;

}

// 执行线程任务

for (int i = 0; i < threadAry.length; i++) {

threadAry[i].start();

} }

}

发表回复

您的电子邮箱地址不会被公开。