1. join强占cpu
public class Solution {
public static void joinWay() {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(500);
System.out.println("Thread t1");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
// 等待t1执行完成
t1.join();
System.out.println("Thread t2");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
try {
// 等待t2执行完成
t2.join();
System.out.println("Thread t3");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
t2.start();
t3.start();
}
public static void main(String[] args) {
joinWay();
}
}
2. 使用wait/notify,这里直接套用之前项目中的大致逻辑
背景是相机拍照连拍,拍照首先要和系统层交互,连拍指令丢给系统层后,返回顺序不确定,比如按照1 2 3 4的顺序,系统层处理完返回的可能是2 1 4 3,需求是按照顺序插入数据库和相册,所以需要按照标号顺序来顺序执行逻辑
/** 定义锁,标识执行到第几个线程 */
public class Lock {
public Lock() {
curNum = 0;
}
// 当前执行的顺序
public int curNum;
}
定义一个函数,每个线程中调用,判断当前是否轮到自己的标号执行,如果轮到自己,则执行逻辑,执行完唤醒阻塞的线程重新竞争锁;如果没轮到自己,直接wait()释放锁
public static void synWay(int index) {
while (true) {
synchronized (lock) {
if (index == lock.curNum) {
System.out.println(index);
lock.curNum ++;
// 执行完,释放锁
lock.notifyAll();
} else {
// 不相等,则释放锁
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
3. lock & condition
notify效率不高,每次只能随机唤醒一个等待的线程,当等待线程较多时,可能出现下一个序号执行的线程,在众多线程被唤醒后判断不匹配又释放锁后,很久才被唤醒。
想到ReentrantLock搭配condition,可以实现选择性通知,规避掉notify中间的无效唤醒时间。
但存在一个问题,有多少个序号,就需要提前准备多少个condition对象,序号很多的情况下,对内存不友好。
ReentrantLock lock = new ReentrantLock();
int number = 0;
Condition[] conditions = new Condition[3];
{
conditions[0] = lock.newCondition();
conditions[1] = lock.newCondition();
conditions[2] = lock.newCondition();
}
/** 启动3个线程 */
public static void reenWay() throws InterruptedException {
Solution s = new Solution();
new Thread(() -> {
try {
s.f(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
s.f(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
s.f(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
public void f(int index) throws InterruptedException {
lock.lock();
try {
while (number != index) {
conditions[index].await();
}
System.out.println("num:" + index);
Thread.sleep(100);
if (index != conditions.length - 1) {
number++;
conditions[index + 1].signal();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
4. 使用单线程线程池
static ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); public static void singleThreadWay() {
for (int i = 0; i < 10; i++) {
int finalI1 = i;
singleThreadExecutor.submit(new Runnable() {
@Override public void run() {
try {
Thread.sleep(100);
System.out.println(finalI1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
5. CountDownLatch
CountDownLatch可以应对一些先并发再统一处理的逻辑,比如大文件分块下载,下载完毕后对文件进行整合、解压等操作。
CountDownLatch维护了一个计数器,可以在构造方法中赋初始值,线程调用countDown()方法原子减,调用await()方法的线程一直阻塞,直到latch值减为0。
public class CountDownLatchEx {
final static CountDownLatch latch = new CountDownLatch(2);
private static class SampleThread extends Thread {
@Override
public void run() {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("SampleThread down");
}
}
private static class WorkThread extends Thread {
private final String threadName;
private final int sleepTime;
public WorkThread(String name, int sleepTime) {
this.threadName = name;
this.sleepTime = sleepTime;
}
@Override
public void run() {
System.out.println("WorkThread " + threadName + " start");
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
System.out.println("WorkThread down");
}
}
public static void main(String[] args) {
new SampleThread().start();
new WorkThread("A", 5000).start();
new WorkThread("B", 2000).start();
}
}