1.继承了Thread(包含了匿名内部类的方法)
2.实现了Runnable(包含了匿名内部类的方式)
3.基于lambda
4.基于线程池
Runnable与Callable的区别:
Runnable关注的是这个过程,不关注执行结果
Runnable提供的run方法,返回值类型是void
Callable关注过程,也关注结果
Callab提供的call方法,返回值就是线程执行任务得到的结果
举例说明:
创建一个新线程,用新线程实现1+2+3+4+…+1000;
使用Thread写的代码:
public class Test16 {
public static int sum = 0;
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
int result = 0;
for (int i = 1; i <= 1000; i++) {
result+=i;
}
sum = result;
}
});
t.start();
t.join();
System.out.println("sum=" + sum);
}
}
这样写的代码不美观,且如果有多个线程就要创建多个成员变量来接收result。
使用Callable写的代码:
public class Test17 {
public static void main(String[] args) throws ExecutionException,InterruptedException {
Callable<Integer> callable = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int result = 0;
for (int i = 1; i <= 1000 ; i++) {
result+=i;
}
return result;
}
};
FutureTask<Integer> task = new FutureTask<>(callable);
Thread t = new Thread(task);
t.start();
System.out.println(task.get());
}
代码解释:
创建?个匿名内部类, 实现 Callable 接?. Callable 带有泛型参数. 泛型参数表?返回值的类型.
? 重写 Callable 的 call ?法, 完成累加的过程. 直接通过返回值返回计算结果.
? 把 callable 实例使? FutureTask 包装?下.
? 创建线程, 线程的构造?法传? FutureTask . 此时新线程就会执? FutureTask 内部的 Callable 的call ?法, 完成计算. 计算结果就放到了FutureTask 对象中.
? 在主线程中调? futureTask.get() 能够阻塞等待新线程计算完毕. 并获取到 FutureTask 中的结果。
FutureTask类,作为Thread和callable的粘合剂
理解 Callable
Callable 和 Runnable 相对, 都是描述?个 “任务”. Callable 描述的是带有返回值的任务, Runnable描述的是不带返回值的任务.
Callable 通常需要搭配 FutureTask 来使?. FutureTask ?来保存 Callable 的返回结果. 因为Callable 往往是在另?个线程中执?的, 啥时候执?完并不确定.
FutureTask 就可以负责这个等待结果出来的?作.
理解 FutureTask
想象去吃?辣烫. 当餐点好后, 后厨就开始做了. 同时前台会给你?张 “?票” . 这个?票就是FutureTask. 后?我们可以随时凭这张?票去查看??的这份?辣烫做出来了没.
可重入互斥锁,和synchronized定位类似,都是来实现互斥效果,保证线程安全
ReentrantLock也是可重入锁,
ReentrantLock的用法:
lock( ) : 加锁,如果获取不到锁就死等
trylock(超时间):加锁,如果获取不到锁,等待一段时间之后就放弃加锁
unlock():解锁
ReentrantLock lock = new ReentrantLock();
-----------------------------------------
lock.lock();
try {
// working
} finally {
lock.unlock()
}
ReentrantLock和synchronized的区别
1.ReentrantLock提供了tryLock操作
2.ReentrantLock提供了公平锁的实现
synchronized是非公平锁
ReentrantLock构造方法中填写参数,就可以设置成公平锁
3.搭配的等待通知机制不同的
对于synchronized搭配wait/notify
对于ReentrantLock搭配Condition类,功能比wait notify略强一点
信号量,用来表示“可用资源的个数”,本质上就是一个计时器
如何理解信号量
可以把信号量想象成是停?场的展?牌: 当前有?位 100 个. 表?有 100 个可?资源.
当有?开进去的时候, 就相当于申请?个可?资源, 可??位就 -1 (这个称为信号量的 P 操作)
当有?开出来的时候, 就相当于释放?个可?资源, 可??位就 +1 (这个称为信号量的 V 操作)
如果计数器的值已经为 0 了, 还尝试申请资源, 就会阻塞等待, 直到有其他线程释放资源.
Semaphore 的 PV 操作中的加减计数器操作都是原?的, 可以在多线程环境下直接使?.
代码示例
? 创建 Semaphore ?例, 初始化为 4, 表?有 4 个可?资源.
? acquire ?法表?申请资源(P操作), release ?法表?释放资源(V操作)
? 创建 20 个线程, 每个线程都尝试申请资源, sleep 1秒之后, 释放资源. 观察程序的执?效果.
public class Test18 {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(4);
Runnable runnable = new Runnable() {
@Override
public void run() {
try{
System.out.println("申请资源");
semaphore.acquire();
System.out.println("获取到了资源");
Thread.sleep(1000);
System.out.println("我释放了资源");
semaphore.release();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
};
for (int i = 0; i < 20; i++) {
Thread t = new Thread(runnable);
t.start();
}
}
}
同时等待 N 个任务执?结束.
好像跑步?赛,10个选?依次就位,哨声响才同时出发;所有选?都通过终点,才能公布成绩。
? 构造 CountDownLatch 实例, 初始化 10 表?有 10 个任务需要完成.
? 每个任务执?完毕, 都调? latch.countDown() . 在 CountDownLatch 内部的计数器同时?
减.
? 主线程中使? latch.await(); 阻塞等待所有任务执?完毕. 相当于计数器为 0 了.
import java.util.Random;
import java.util.concurrent.CountDownLatch;
public class Test19 {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
int id = i;
Thread t = new Thread(()->{
Random random = new Random();
int time = (random.nextInt(5)+1)*1000;
System.out.println("线程"+id+"开始下载");
try {
Thread.sleep(time);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("线程"+id+"下载完成");
countDownLatch.countDown();
});
t.start();
}
countDownLatch.await();
System.out.println("全部下载完成");
}
}