创建线程的四种方式:
Runnable 缺少的一项功能是,当线程 终止时(run完成时),我们无法使线程返回结果。为了支持此功能, Java 中提供了 Callable 接口。
Callable 接口的特点如下(重点)
Runnable和Callable区别:
**public boolean cancel(boolean mayInterrupt)*用于停止任务。 ==如果尚未启动,它将停止任务。如果已启动,则仅在 mayInterrupt 为 true
时才会中断任务。==
public Object get()抛出 InterruptedException,ExecutionException: 用于获取任务的结果。
==如果任务完成,它将立即返回结果,否则将等待任务完成,然后返回结果。 ==
public boolean isDone():如果任务完成,则返回 true,否则返回 false
可以看到 Callable 和 Future 做两件事-Callable 与 Runnable 类似,因为它封 装了要在另一个线程上运行的任务,而 Future 用于存储从另一个线程获得的结 果。实际上,future 也可以与 Runnable 一起使用。
要创建线程,需要 Runnable。为了获得结果,需要 future。
核心原理:
Thread并不能直接使用Callable进行构造:
所以这里使用FutureTask:他既实现了Runnable,又构造包含了Callable。
class MyThread3 implements Callable {
@Override
public Object call() throws Exception {
return 1024;
}
}
// FutureTask
FutureTask<Integer> futureTask = new FutureTask<>(new MyThread3());
FutureTask<Integer> futureTask2 = new FutureTask<>(()->1024);
FutureTask原理:未来任务
开启单线程去完成一个耗时长的支线任务,不影响主线任务。最后将这些任务都汇总起来,只汇总一次。
FutureTask<Integer> futureTask2 = new FutureTask<>(()->{
System.out.println(Thread.currentThread().getName() +" come in callable");
return 1024;
});
new Thread(futureTask2,"futureTask2").start();
while (!futureTask2.isDone()){
System.out.println(Thread.currentThread().getName()+" wait!");
}
System.out.println(futureTask2.get());
System.out.println(Thread.currentThread().getName()+" over!");
在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些 作业交给 Future 对象在后台完成, 当主线程将来需要时,就可以通过 Future 对象获得后台作业的计算结果或者执行状态
JUC 中提供了三种常用的辅助类,通过这些辅助类可以很好的解决线程数量过 多时 Lock 锁的频繁操作。这三种辅助类为:
//演示 CountDownLatch
public class CountDownLatchDemo {
//6个同学陆续离开教室之后,班长锁门
public static void main(String[] args) throws InterruptedException {
//创建CountDownLatch对象,设置初始值
CountDownLatch countDownLatch = new CountDownLatch(6);
//6个同学陆续离开教室之后
for(int i=1;i<=6;i++){
new Thread(()->{
System.out.println(Thread.currentThread().getName()+" 号同学离开");
countDownLatch.countDown();
},String.valueOf(i)).start();
}
//等待
countDownLatch.await();
System.out.println(Thread.currentThread().getName()+" 班长锁门走人了");
}
}
//集齐7颗龙珠就可以召唤神龙
public class CyclicBarrierDemo {
//创建固定值
private static final int NUMBER = 7;
public static void main(String[] args) {
//创建CyclicBarrier
CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER,()->{
System.out.println("收集7颗龙珠就可以召唤神龙");
});
//集齐七颗龙珠过程
for(int i=1;i<=7;i++){
new Thread(()->{
System.out.println(Thread.currentThread().getName()+" 星龙珠被收集");
try {
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
}
注意:如果循环的次数不是7,大于或小于7,在打印完之后,线程不会停止。
public class SemaphoreDemo {
public static void main(String[] args) {
//创建Semaphore,设置许可数量
Semaphore semaphore = new Semaphore(3);
//模拟6辆汽车
for (int i = 1; i <=6; i++) {
new Thread(()->{
try {
//抢占
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+" 抢到了车位");
//设置随机停车时间
TimeUnit.SECONDS.sleep(new Random().nextInt(5));
System.out.println(Thread.currentThread().getName()+" ------离开了车位");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}
// 执行结果:
1 抢到了车位
2 抢到了车位
3 抢到了车位
1 ------离开了车位
4 抢到了车位
4 ------离开了车位
5 抢到了车位
2 ------离开了车位
6 抢到了车位
5 ------离开了车位
3 ------离开了车位
6 ------离开了车位