六、JUC强大的辅助类
6.1 多线程中创建线程的方式
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 多线程中创建线程的方式
* 1、 继承Thread类
* 2、 实现Runnable接口
* 3、 实现Callable接口
* 4、 实现Callable接口通过FutureTask包装器来创建Thread线程
*/
public class CallableDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask futureTask = new FutureTask(new MyThread());
System.out.println(Thread.currentThread().getName()+"计算完成");
//***Callable*** 只调用了一次
new Thread(futureTask, "A").start();
new Thread(futureTask, "B").start();
System.out.println(futureTask.get());
}
}
在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些作业交给
Future对象
在后台完成,当主线程将来需要时,就可以通过Future对象
获得后台作业的计算结果或者执行状态。一般FutureTask
多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。仅在计算完成时才能检索结果;如果计算尚未完成,则阻塞get
方法。一旦计算完成,就不能再重新开始或取消计算。get
方法而获取结果只有在计算完成时获取,否则会一直阻塞直到任务转入完成状态,然后会返回结果或者抛出异常。 只计算一次get
方法放到最后
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
/**
* Callable接口与Runnable接口的异同
* 1、 是否有返回值
* 2、 是否抛异常
* 3、 落地方法不一样,一个是run,一个是call
*/
class MyThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
try { TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}
System.out.println("***Callable***");
return 1024;
}
}
class MyThread2 implements Runnable{
@Override
public void run() {
}
}
6.2 CountDownLatch
import java.util.concurrent.CountDownLatch;
/**
* CyclicBarrier的字面意思是可循环(Cyclic)使用的屏障(Barrier)。
* 它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞
* 直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。
* 线程进入屏障通过CyclicBarrier的await()方法。
*/
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t离开教室");
countDownLatch.countDown();
}, String.valueOf(i)).start();
}
countDownLatch.await();
System.out.println(Thread.currentThread().getName() + "\t班长关门");
}
private static void CloseDoor() {
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t离开教室");
}, String.valueOf(i)).start();
}
System.out.println(Thread.currentThread().getName() + "\t班长关门");
}
}
6.3 Semaphore
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* 在信号量上我们定义两种操作:
* acquire(获取): 当一个线程调用acquire操作时,它要么通过成功获取信号量(信号量减1),要么一直等下去,直到有线程释放信号量,或超时。
* release(释放):实际上会将信号量的值加1,然后唤醒等待的线程。
* 信号量主要用于两个目的,一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制。
*/
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(1);//模拟资源类,有三个空车位
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "\t抢占了车位");
//暂停一会线程
try { TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}
System.out.println(Thread.currentThread().getName() + "\t离开了车位");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();
}
}, String.valueOf(i)).start();
}
}
}