并发编程的重点:多线程之间的同步,依赖synchornized,wait & notify,AQS和基于Lock的各种实现。
9种方法实现多线程的顺序执行,定义thread1、thread2、thread3,要求执行顺序为:thread1 > thread2 > thread3
1 使用线程的join方法
线程依次join前序执行的线程;
Thread thread1 = new Thread(
() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread1 finished");
}
);
Thread thread2 = new Thread(
() -> {
try {
thread1.join();
Thread.sleep(1000);
System.out.println("thread2 finished");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
);
Thread thread3 = new Thread(
() -> {
try {
thread2.join();
System.out.println("thread3 finished");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
);
thread3.start();
thread2.start();
thread1.start();
2 使用主线程的join方法
Thread thread1 = new Thread(
() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread1 finished");
}
);
Thread thread2 = new Thread(
() -> {
try {
thread1.join();
Thread.sleep(1000);
System.out.println("thread2 finished");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
);
Thread thread3 = new Thread(
() -> {
try {
thread2.join();
System.out.println("thread3 finished");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
);
thread1.start();
thread1.join();
thread2.start();
thread2.join();
thread3.start();
thread3.join();
3 使用对象的wait和notify方法
设置锁对象:lock
设置线程的执行状态:state
thread1、thread2、thread3竞争lock;
如果thread2先获得lock,判断thread1是否已经执行完毕,如果thread1没有执行完毕,那么thread2 wait,如果thread1已经执行完毕,thread2可以执行。
如果thread3先获得lock,判断thread2是否已经执行完毕,如果thread2没有执行完毕,那么thread3 wait,如果thread2已经执行完毕,thread3可以执行。
thread1执行完毕后,唤醒thread2和thread3。
Object lock = new Object();
AtomicInteger state = new AtomicInteger(0);
Thread thread1 = new Thread(
() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock) {
System.out.println("thread1 got lock");
System.out.println("thread1 finished");
state.addAndGet(1);
lock.notifyAll();
}
}
);
Thread thread2 = new Thread(
() -> {
synchronized (lock) {
System.out.println("thread2 got lock");
while (state.get() < 1) {
try {
System.out.println("thread2 waiting");
lock.wait();
System.out.println("thread2 wakeup");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("thread2 finished");
state.addAndGet(1);
lock.notifyAll();
}
}
);
Thread thread3 = new Thread(
() -> {
synchronized (lock) {
System.out.println("thread3 got lock");
while (state.get() < 2) {
try {
System.out.println("thread3 waiting");
lock.wait();
System.out.println("thread3 wakeup");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("thread3 finished");
state.addAndGet(1);
}
}
);
thread3.start();
thread2.start();
thread1.start();
4 使用线程池
使用单线程的线程池
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(() -> {
System.out.println("thread1 finished");
});
executorService.submit(() -> {
System.out.println("thread2 finished");
});
executorService.submit(() -> {
System.out.println("thread3 finished");
});
executorService.shutdown();
5 使用AQS的Condition(条件变量)
一个锁,两个条件;thread2等待condition1、thread3等待condition2
ReentrantLock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
ReentrantLock reentrantLock = new ReentrantLock();
Condition c1 = reentrantLock.newCondition();
Condition c2 = reentrantLock.newCondition();
Thread thread1 = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
reentrantLock.lock();
System.out.println("thread1 got lock");
System.out.println("thread1 finished");
c1.signal();
reentrantLock.unlock();
});
Thread thread2 = new Thread(() -> {
reentrantLock.lock();
System.out.println("thread2 got lock");
try {
c1.await();
System.out.println("thread2 wakeup");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread2 finished");
c2.signal();
reentrantLock.unlock();
});
Thread thread3 = new Thread(() -> {
reentrantLock.lock();
System.out.println("thread3 got lock");
try {
c2.await();
System.out.println("thread3 wakeup");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread3 finished");
reentrantLock.unlock();
});
thread3.start();
thread2.start();
thread1.start();
6 使用CountDownLatch
两个Latch,thread2等待latch2、thread3等待latch3
CountDownLatch latch2 = new CountDownLatch(1);
CountDownLatch latch3 = new CountDownLatch(1);
Thread thread1 = new Thread(
() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread1 finished");
latch2.countDown();
}
);
Thread thread2 = new Thread(
() -> {
try {
Thread.sleep(1000);
latch2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread2 finished");
latch3.countDown();
}
);
Thread thread3 = new Thread(
() -> {
try {
latch3.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread3 finished");
}
);
thread3.start();
thread2.start();
thread1.start();
7 使用CyclicBarrier(回环栅栏)
通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。暂且把这个状态就叫做barrier,当调用await()方法之后,线程就处于barrier了。
CyclicBarrier cyclicBarrier2 = new CyclicBarrier(2, () -> {System.out.println("thread1 and thread2 reached same await status");});
CyclicBarrier cyclicBarrier3 = new CyclicBarrier(2, () -> {System.out.println("thread2 and thread3 reached same await status");});
Thread thread1 = new Thread(
() -> {
try {
System.out.println("thread1 finished");
cyclicBarrier2.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
);
Thread thread2 = new Thread(
() -> {
try {
cyclicBarrier2.await();
System.out.println("thread2 finished");
cyclicBarrier3.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
);
Thread thread3 = new Thread(
() -> {
try {
cyclicBarrier3.await();
System.out.println("thread3 finished");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
);
thread3.start();
thread2.start();
thread1.start();
8 使用Semaphore(信号量)
Semaphore是一个计数信号量,从概念上将,Semaphore包含一组许可证,如果有需要的话,每个acquire()方法都会阻塞,直到获取一个可用的许可证,每个release()方法都会释放持有许可证的线程,并且归还Semaphore一个可用的许可证。然而,实际上并没有真实的许可证对象供线程使用,Semaphore只是对可用的数量进行管理维护。
前序线程执行之后,向信号量release一个许可证,后序线程通过acquire获取许可证,没有获取到则阻塞,获取到则执行
Semaphore semaphore2 = new Semaphore(0);
Semaphore semaphore3 = new Semaphore(0);
Thread thread1 = new Thread(
() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread1 finished");
semaphore2.release();
}
);
Thread thread2 = new Thread(
() -> {
try {
Thread.sleep(1000);
semaphore2.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread2 finished");
semaphore3.release();
}
);
Thread thread3 = new Thread(
() -> {
try {
semaphore3.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread3 finished");
}
);
thread3.start();
thread2.start();
thread1.start();
9 使用CompletableFuture
CompletableFuture completableFuture = new CompletableFuture();
completableFuture.runAsync(() -> {System.out.println("thread1 finished");})
.thenRunAsync(() -> {System.out.println("thread2 finished");})
.thenRunAsync(() -> {System.out.println("thread3 finished");});