Callable
前言
线程Runnable和Callable的主要区别
解决方法:
两者区别主要以下两点
1.Runnable接口的run()方法没有返回值,而Callable接口的call()方法可以有返回值。
2…Runnable接口的run()方法不可以声明抛出异常,而Callable接口的call()方法可以声明抛出异常。
-
public interface Callable<V>
返回结果并可能引发异常的任务。实现者定义一个没有参数的单一方法,称为
call
。Callable
接口类似于[Runnable
] ,因为它们都是为其实例可能由另一个线程执行的类设计的。 然而,ARunnable
不返回结果,也不能抛出被检查的异常。该[
Executors
]类包含的实用方法,从其他普通形式转换为Callable
类。
- 可以有返回值
- 可以抛出异常
- 方法不同 run()
package com.nie.juc.callable;/*
*
*@auth wenzhao
*@date 2021/4/21 12:51
*/
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
new Thread().start(); // 怎么启动Callable
thread thread = new thread();
FutureTask futureTask = new FutureTask<>(thread);//适配类
new Thread(futureTask, "A").start();
new Thread(futureTask, "B").start();//结果会被缓存,效率高
Integer o = (Integer) futureTask.get(); // 返回值
// 这个get 方法可能会产生阻塞!把他放到最后
// 或者使用异步通信来处理!
System.out.println(o);
}
}
class thread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("call()");
return 1024;
}
}
//newThread(new Runnable()).start();
//newThread(new FutureTask<V>()).start();
//newThread(new FutureTask<V>(Callable)).start();
thread 只能实现runable接口及其子类 futureTask实现了runnable,futureTask
有个带callable参数的构造器,因此关联了
细节:
1、有缓存
由于jvm第二次在调用
FutureTask
对象所持有的线程,此时FutureTask
的state
此时非NEW状态,则此时会直接结束对应的线程,就会导致任务不执行, 只是在第一次调用时返回结果保存了,
2、结果可能需要等待,会阻塞!
这个看一眼FutureTask源码就清楚了,用的是一个state存放线程状态,outcome存放结果
学习参考链接
常用的辅助类(重点)
1.CountDownLatch
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
// 总数是6,必须要执行任务的时候,再使用!
CountDownLatch countDownLatch = new CountDownLatch(7);
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " Go out");
countDownLatch.countDown(); // 数量-1
}, String.valueOf(i)).start();
}
countDownLatch.await(); // 等待计数器归零,然后再向下执行
System.out.println("Close Door");
}
}
注解:
==countDownLatch.countDown()==数量减一
countDownLatch.await() 等待计数器归零,然后在向下执行
每次有线程调用countDown()数量-1,假设计数器变为0,countDownLatch.await()就会被唤醒,继续
执行!
允许一个或多个线程等待直到在其他线程中执行的一组操作完成的同步辅助。
A
CountDownLatch
用给定的计数初始化。await
方法阻塞,直到由于countDown()
方法的调用而导致当前计数达到零,之后所有等待线程被释放,并且任何后续的await
调用立即返回。 这是一个一次性的现象 - 计数无法重置。 如果您需要重置计数的版本,请考虑使用CyclicBarrier
。A
CountDownLatch
是一种通用的同步工具,可用于多种用途。 一个CountDownLatch
为一个计数的CountDownLatch用作一个简单的开/关锁存器,或者门:所有线程调用await
在门口等待,直到被调用countDown()
的线程打开。 一个CountDownLatch
初始化N可以用来做一个线程等待,直到N个线程完成某项操作,或某些动作已经完成N次。
CountDownLatch
一个有用的属性是,它不要求调用countDown
线程等待计数到达零之前继续,它只是阻止任何线程通过await
,直到所有线程可以通过。
2、CyclicBarrier
加法计数器
用于召唤神龙
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier=new CyclicBarrier(7,()->
System.out.println("可以召唤神龙了"));
for (int i = 1; i <=7 ; i++) {
final int temp=i;
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"收集"+temp+"个龙珠");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
3. Semaphore
Semaphore信号量
例如:排队蹲坑, 6人 三坑
package com.nie.juc.auxiliary;/*
*
*@auth wenzhao
*@date 2021/4/21 16:31
*/
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreDemo {
public static void main(String[] args) {
线程量: 坑位 限流
Semaphore semaphore=new Semaphore(3);
for (int i = 1; i <=6 ; i++) {
new Thread(()->{
//acquire() //得到
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"占到了坑");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"释放坑");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}
==semaphore.acquire();==获得,假设如果已经满了,等待,等待被释放为止
==semaphore.release();==释放,会将当前的信号量释放+1…然后唤醒等待的线程
作用: 多个共享资源互斥的使用!!! 并发限流,控制最大的线程数
补充:信号量与互斥锁之间的区别:
(1):互斥量用于线程的互斥,信号线用于线程的同步。这是互斥量和信号量的根本区别,也就是互斥和同步之间的区别。
(2):互斥量值只能为0/1,信号量值可以为非负整数。
也就是说,一个互斥量只能用于一个资源的互斥访问,它不能实现多个资源的多线程互斥问题。
信号量可以实现多个同类资源的多线程互斥和同步。当信号量为单值信号量是,也可以完成一个资源的互斥访问。信号量是通过一个计数器控制对共享资源的访问,信号量的值是一个非负整数,所有通过它的线程都会将该整数减一。如果计数器大于0,则访问被允许,计数器减1;如果为0,则访问被禁止,所有试图通过它的线程都将处于等待状态。
计数器计算的结果是允许访问共享资源的通行证。因此,为了访问共享资源,线程必须从信号量得到通行证, 如果该信号量的计数大于0,则此线程获得一个通行证,这将导致信号量的计数递减,否则,此线程将阻塞直到获得一个通行证为止。当此线程不再需要访问共享资源时,它释放该通行证,这导致信号量的计数递增,如果另一个线程等待通行证,则那个线程将在那时获得通行证。
(3):互斥量的加锁和解锁必须由同一线程分别对应使用,信号量可以由一个线程释放,另一个线程得到。