目录
[synchronized 和 ReentrantLock之间的区别](#synchronized 和 ReentrantLock之间的区别)
JUC(java.util.concurrent) 的常⻅类
Callable接口
Callable 是⼀个 interface . 相当于把线程封装了⼀个 "返回值". ⽅便程序猿借助多线程的⽅式计算结果
Callable接口和Runnable接口并列关系
返回值call()
泛型参数
代码⽰例: 创建线程计算 1 + 2 + 3 + ... + 1000, 使⽤ Callable 版本
java
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class Demo40 {
public static void main(String[] args) {
//此处Callable只是定义了一个"带有返回值"的任务
//并没有真的在执行,执行还是需要搭配Thread对象
Callable<Integer> callable = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int ret = 0;
for (int i = 1; i <= 100; i++) {
ret += i;
}
return ret;
}
};
FutureTask<Integer> futureTask = new FutureTask<>(callable);
Thread t = new Thread(futureTask);
t.start();
System.out.println(futureTask.get());
}
}
Thread的构造方法,没有提供版本传入Callable对象
Thread本身不提供获取结果的方法
就需要凭FutureTask对象来拿到结果
**代码⽰例: ** 创建线程计算 1 + 2 + 3 + ... + 1000, 不使⽤ Callable 版本
java
public class Demo41 {
private static int total = 0;
public static void main(String[] args) throws InterruptedException {
Runnable runnable = new Runnable() {
@Override
public void run() {
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += 0;
}
total = sum;
}
};
Thread t =new Thread(runnable);
t.start();
t.join();
System.out.println(total);
}
}
理解 Callable
Callable 和 Runnable 相对, 都是描述⼀个 "任务". Callable 描述的是带有返回值的任务, Runnable
描述的是不带返回值的任务.
Callable 通常需要搭配 FutureTask 来使⽤. FutureTask ⽤来保存 Callable 的返回结果. 因为
Callable 往往是在另⼀个线程中执⾏的, 啥时候执⾏完并不确定.
FutureTask 就可以负责这个等待结果出来的⼯作.
理解 FutureTask
想象去吃⿇辣烫. 当餐点好后, 后厨就开始做了. 同时前台会给你⼀张 "⼩票" . 这个⼩票就是
FutureTask. 后⾯我们可以随时凭这张⼩票去查看⾃⼰的这份⿇辣烫做出来了没
ReentrantLock
可重⼊互斥锁. 和 synchronized 定位类似, 都是⽤来实现互斥效果, 保证线程安全.
和synchronized是并列关系
更经典风格的锁
java
import java.util.concurrent.locks.ReentrantLock;
public class Demo42 {
private static int count = 0;
public static void main(String[] args) {
ReentrantLock locker = new ReentrantLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5000; i++) {
locker.lock();
try{
count++;
}finally {
locker.unlock();
}
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5000; i++) {
locker.lock();
try{
count++;
}finally {
locker.unlock();
}
}
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
synchronized 和 ReentrantLock之间的区别
1.synchronized 是关键字(内部实现是jvm内部通过c++实现的)ReentrantLock标准库的类(Java)
2.synchronized通过代码块控制加锁解锁,ReentrantLock需要lock/unlock方法,需要注意unlock不被调用的问题
3.ReentrantLock除了提供lock,unlock之外还提供了一个方法tryLock()
tryLock不会阻塞
加锁成功返回true,失败返回false
调用者判定返回值决定接下来咋做
设置超时时间
等待时间达到超时时间在返回true/false
4.ReentrantLock提供了公平锁的实现
java
ReentrantLock locker = new ReentrantLock(true);
默认是非公平的
5.ReentrantLock搭配的等待通知机制是Condition类,相比wait,notify来说功能更强大一些
信号量Semaphore
信号量, ⽤来表⽰ "可⽤资源的个数". 本质上就是⼀个计数器
能够协调多个进程之间的资源分配
也能协调多个线程之间的资源分配
申请acquire一个资源,计数器就会-1,p操作
释放release一个资源,计数器就会+1,v操作
计数器为0,继续申请就会阻塞等待
java
import java.util.concurrent.Semaphore;
public class Demo43 {
public static void main(String[] args) throws InterruptedException {
Semaphore semaphore = new Semaphore(4);
semaphore.acquire();
System.out.println("进行一次p操作");
semaphore.acquire();
System.out.println("进行一次p操作");
semaphore.acquire();
System.out.println("进行一次p操作");
semaphore.acquire();
System.out.println("进行一次p操作");
}
}
信号量的一个特殊情况,初始值为1 的信号量
取值要么是1要么是0(二元信号量)
等价于"锁"(普通的信号量,就相当于锁的更广泛的推广)
如果是普通的N的信号量,就可以限制同时有多少个线程来执行某个逻辑
java
import java.util.concurrent.Semaphore;
public class Demo44 {
private static int count = 0;
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(1);
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5000; i++) {
try {
semaphore.acquire();
count++;
semaphore.release();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5000; i++) {
try {
semaphore.acquire();
count++;
semaphore.release();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
});
t1.start();
t2.start();
}
}
CountDownLatch
同时等待N个任务执行结束
使用多线程经常把一个大的任务拆分成多个子任务
使用多线程执行这些子任务,从而提高程序的效率
1.构造方法指定参数,描述拆成了多少个任务
2.每个任务执行完毕之后,都调用一次countDown方法
如果指定10个任务,当一共调用了10次countDown说明任务全都完成了
3.主线程中调用await方法,等待所以任务执行完毕
await就会返回/阻塞等待
java
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo45 {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(10);
ExecutorService executor = Executors.newFixedThreadPool(4);
for (int i = 0; i < 10; i++) {
int id = i;
executor.submit(()->{
System.out.println("子任务开始执行"+id);
try {
Thread.sleep(1000);
}catch (InterruptedException e){
throw new RuntimeException(e);
}
System.out.println("子任务结束执行"+id);
});
}
latch.await();//阻塞等待所有的任务结束
System.out.println("所有任务执行完毕");
executor.shutdown();
}
}