Java多线程
多线程基础
一、创建线程
仅仅只有new Thread
这种方法创建线程
Java中无法销毁一个线程,只能表现一个线程的状态。
通过thread.start()
启动线程(仅仅只是通知线程启动)
thread.join()
用于控制线程必须执行完成,调整优先级并不能保证优先级高的线程先执行。
1、继承Thread
- 继承
Thread
- 覆盖
run()
方法 - 直接调用
Thread#start()
执行
public class MyThread extends Thread{
@Override
public void run() {
System.out.println("Thread");
}
}
public class ThreadMain {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
2、实现Runnable
- 实现
Runnable
- 获取实现
Runnable
接口的实例,作为参数,创建Thread
实例 - 执行
Thread#start()
启动线程
public class RunnableThread implements Runnable{
@Override
public void run() {
System.out.println("Runnable");
}
}
public class ThreadMain {
public static void main(String[] args) {
Thread thread = new Thread(new RunnableThread());
thread.start();
}
}
3、Callable
和Future
创建线程
Callable
相对于Runnable
会有一个线程返回值
- 实现
Callable
接口 - 以
Callable
的实现类为参数,创建FutureTask
实例 - 将
FutureTask
作为Thread
的参数,创建Thread
实例 - 通过
Thread#start
启动线程 - 通过
FutureTask#get()
阻塞获取线程的返回值
public class CallableThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("Callable");
return 90;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<Integer> futureTask = new FutureTask<Integer>(new CallableThread());
Thread thread = new Thread(futureTask, "Thread1");
thread.start();
System.out.println(futureTask.get());
}
}
4、使用线程池创建
public class MultiThreadStudy {
public static void main(String[] args) {
int corePoolSize = 5;
int maximumPoolSize = 10;
long keepAliveTime = 2;
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue <Runnable>(3);
ThreadPoolExecutor threadPoolExecutor = null;
/**
* ThreadPoolExecutor 线程池,几个参数的含义如下:
* int corePoolSize, 核心线程数大小
* int maximumPoolSize, 最大线程数
* long keepAliveTime, 存活时间
* TimeUnit unit, keepAliveTime的时间单位
* BlockingQueue<Runnable> workQueue, 存放待执行任务的队列
* RejectedExecutionHandler handler 拒绝策略
*/
try {
threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue,
new ThreadPoolExecutor.AbortPolicy());
//循环提交任务
for (int i = 0; i < 13; i++) {
//提交任务的索引
final int index = (i + 1);
threadPoolExecutor.submit(() -> {
//线程打印输出
System.out.println("大家好,我是线程:" + index);
try {
//模拟线程执行时间,10s
Thread.sleep(7000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
//每个任务提交后休眠500ms再提交下一个任务,用于保证提交顺序
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPoolExecutor.shutdown();
}
}
}
5、通过Java
创建进程
public class ProcessCreationQuestion {
public static void main(String[] args) throws IOException {
// 获取 Java Runtime
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec("cmd /k start http://www.baidu.com");
process.exitValue();
}
}
二、停止线程
无法真正停止一个线程,真正停止的只能是线程的逻辑。
请说明Thread interrupt()、isInterrupted()、interrupted()
的区别以及意义?
Thread interrupt(): 设置状态,调JVM的本地(native)interrupt0()
方法。
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
//--> private native void interrupt0();
b.interrupt(this);
return;
}
}
interrupt0();
}
isInterrupted()
: 调的是静态方法isInterrupted()
,当且仅当状态设置为中断时,返回false
,并不清除状态。
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
public boolean isInterrupted() {
return isInterrupted(false);
}
interrupted(): 私有本地方法,即判断中断状态,又清除状态。
private native boolean isInterrupted(boolean ClearInterrupted);
三、线程异常
当线程遇到异常的时候,线程会挂
public class ThreadExceptionQuestion {
public static void main(String[] args) throws InterruptedException {
//...
// main 线程 -> 子线程
Thread t1 = new Thread(() -> {
throw new RuntimeException("数据达到阈值");
}, "t1");
t1.start();
// main 线程会中止吗?
t1.join();
// Java Thread 是一个包装,它由 GC 做垃圾回收
// JVM Thread 可能是一个 OS Thread,JVM 管理,
// 当线程执行完毕(正常或者异常)
System.out.println(t1.isAlive());
}
}
当线程遇到异常时,如何捕获?
...
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
System.out.printf("线程[%s] 遇到了异常,详细信息:%s\n",
thread.getName(),
throwable.getMessage());
});
...
当线程遇到异常时,ThreadPoolExecutor
如何捕获异常?
public class ThreadPoolExecutorExceptionQuestion {
public static void main(String[] args) throws InterruptedException {
// ExecutorService executorService = Executors.newFixedThreadPool(2);
ThreadPoolExecutor executorService = new ThreadPoolExecutor(
1,
1,
0,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>()
) {
/**
* 通过覆盖 {@link ThreadPoolExecutor#afterExecute(Runnable, Throwable)} 达到获取异常的信息
* @param r
* @param t
*/
@Override
protected void afterExecute(Runnable r, Throwable t) {
System.out.printf("线程[%s] 遇到了异常,详细信息:%s\n",
Thread.currentThread().getName(),
t.getMessage());
}
};
executorService.execute(() -> {
throw new RuntimeException("数据达到阈值");
});
// 等待一秒钟,确保提交的任务完成
executorService.awaitTermination(1, TimeUnit.SECONDS);
// 关闭线程池
executorService.shutdown();
}
}
四、线程状态
新建 -> 就绪 -> 阻塞 -> 运行 -> 死亡
获取当前jvm
所有的现场状态
- 可以直接使用
jstack
命令 ThreadMXBean
public class AllThreadStackQuestion {
public static void main(String[] args) {
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
long[] threadIds = threadMXBean.getAllThreadIds();
for (long threadId : threadIds) {
ThreadInfo threadInfo = threadMXBean.getThreadInfo(threadId);
System.out.println(threadInfo.toString());
}
}
}
获取线程的资源消费情况
public class AllThreadInfoQuestion {
public static void main(String[] args) {
ThreadMXBean threadMXBean = (ThreadMXBean) ManagementFactory.getThreadMXBean();
long[] threadIds = threadMXBean.getAllThreadIds();
for (long threadId : threadIds) {
// ThreadInfo threadInfo = threadMXBean.getThreadInfo(threadId);
// System.out.println(threadInfo.toString());
long bytes = threadMXBean.getThreadAllocatedBytes(threadId);
long kBytes = bytes / 1024;
System.out.printf("线程[ID:%d] 分配内存: %s KB\n", threadId, kBytes);
}
}
}
五、线程同步
synchronized
synchronized
关键字在修饰方法与代码块中的区别:字节码的区别,代码块用monitor
,方法用synchronized
关键字
synchronized
关键字与ReentrantLock
之间的区别:
- 两者都是可重入锁
synchronized
依赖于JVM
,而ReentrantLock
依赖于API
ReentrantLock
比synchronized
增加了一些高级功能- 等待可中断
- 可实现公平锁
- 可实现选择性通知(锁可以绑定多个条件)
偏向锁只对synchronized
有用,而ReentrantLock
已经实现了偏向锁.
六、线程通讯
wait()、notify()、notifyAll()方法是Object的本地final方法,无法被重写
wait()
:获得锁的对象,释放锁,当前线程又被阻塞,等同于Java 5 LockSupport
中的park
方法。
notify()
:已经获得锁,唤起一个被阻塞的线程,等同于Java 5 LockSupport
中的unpark
方法
notifyAll()
:会让所有处于等待池的线程全部进入锁池去竞争获取锁的机会
七、线程退出
当主线程退出时,守护线程不一定执行完毕
public class DaemonThreadQuestion {
public static void main(String[] args) {
// main 线程
Thread t1 = new Thread(() -> {
System.out.println("Hello,World");
// Thread currentThread = Thread.currentThread();
// System.out.printf("线程[name : %s, daemon:%s]: Hello,World\n",
// currentThread.getName(),
// currentThread.isDaemon()
// );
}, "daemon");
// 编程守候线程
t1.setDaemon(true);
t1.start();
// 守候线程的执行依赖于执行时间(非唯一评判)
}
}
ShutdownHook
线程的使用场景,以及触发执行
使用场景:Spring
中AbstractApplicationContext
的registerShutdownHook()
public class ShutdownHookQuestion {
public static void main(String[] args) {
Runtime runtime = Runtime.getRuntime();
runtime.addShutdownHook(new Thread(ShutdownHookQuestion::action, "Shutdown Hook Question"));
}
private static void action() {
System.out.printf("线程[%s] 正在执行...\n", Thread.currentThread().getName()); // 2
}
}
如何确保主线程退出前,所有线程执行完毕?
public class CompleteAllThreadsQuestion {
public static void main(String[] args) throws InterruptedException {
// main 线程 -> 子线程
Thread t1 = new Thread(CompleteAllThreadsQuestion::action, "t1");
Thread t2 = new Thread(CompleteAllThreadsQuestion::action, "t2");
Thread t3 = new Thread(CompleteAllThreadsQuestion::action, "t3");
// 不确定 t1、t2、t3 是否调用 start()
t1.start();
t2.start();
t3.start();
// 创建了 N Thread
Thread mainThread = Thread.currentThread();
// 获取 main 线程组
ThreadGroup threadGroup = mainThread.getThreadGroup();
// 活跃的线程数
int count = threadGroup.activeCount();
Thread[] threads = new Thread[count];
// 把所有的线程复制 threads 数组
threadGroup.enumerate(threads, true);
for (Thread thread : threads) {
System.out.printf("当前活跃线程: %s\n", thread.getName());
}
}
private static void action() {
System.out.printf("线程[%s] 正在执行...\n", Thread.currentThread().getName()); // 2
}
}
J.U.C
并发集合框架
public class ThreadSafeCollectionQuestion {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Set<Integer> set = Set.of(1, 2, 3, 4, 5);
Map<Integer, String> map = Map.of(1, "A");
// 以上实现都是不变对象,不过第一个除外
// 通过 Collections#sychronized* 方法返回
// Wrapper 设计模式(所有的方法都被 synchronized 同步或互斥)
list = Collections.synchronizedList(list);
set = Collections.synchronizedSet(set);
map = Collections.synchronizedMap(map);
list = new CopyOnWriteArrayList<>(list);
set = new CopyOnWriteArraySet<>(set);
map = new ConcurrentHashMap<>(map);
}
}
一、List部分
Vector
:底层方法使用synchronized
进行同步操作,是List
的实现。
CopyOnWriteArrayList
:读的时候不加锁,弱一致性,添加数据的时候使用的是Arrays.copy()
方法进行数据拷贝,和synchronized
来保证线程的安全性。
SynchronizedList
:底层方法使用synchronized
进行同步操作,返回list
,实现原理方式是Wrapper
实现
二、Set部分
SynchronizedSet
:底层方法使用synchronized
进行同步操作
CopyOnWriteArraySet
:底层借助CopyOnWriteArrayList
数组实现。
ConcurrentSkipListSet
:
当 Set#iterator() 方法返回 Iterator 对象后,能否在其迭代中,给 Set 对象添加新的元素?
不一定;Set 在传统实现中,会有fail-fast
问题;而在J.U.C
中会出现弱一致性,对数据的一致性要求较低,是可以给 Set 对象添加新的元素。
三、Map部分
Hashtable
:底层方法使用synchronized
进行同步操作,同步粒度较大。
ConcurrentHashMap
:8以后底层使用CAS
和synchronized
进行同步操作,数据结构为数组 + 链表 + 红黑树
ConcurrentSkipListMap
:
SynchronizedMap
:底层方法使用synchronized
进行同步操作
请说明 ConcurrentHashMap 与 ConcurrentSkipListMap 各自的优势与不足?
在 java 6 和 8 中,ConcurrentHashMap 写的时候,是加锁的,所以内存占得比较小,而 ConcurrentSkipListMap 写的时候是不加锁的,内存占得相对比较大,通过空间换取时间上的成本,速度较快,但比前者要慢,ConcurrentHashMap 基本上是常量时间。ConcurrentSkipListMap 读和写都是\(log N\)实现,高性能相对稳定。
四、Queue部分
请说明 BlockingQueue 与 Queue 的区别?
BlockingQueue 继承了 Queue 的实现;put 方法中有个阻塞的操作(InterruptedException),当队列满的时候,put 会被阻塞;当队列空的时候,put方法可用。take 方法中,当数据存在时,才可以返回,否则为空。
请说明 LinkedBlockingQueue 与 ArrayBlockingQueue 的区别?
LinkedBlockingQueue 是链表结构;有两个构造器,一个是(Integer.MAX_VALUE),无边界,另一个是(int capacity),有边界;ArrayBlockingQueue 是数组结构;有边界。
请说明 LinkedTransferQueue 与 LinkedBlockingQueue 的区别?
LinkedTransferQueue 是java 7中提供的新接口,性能比后者更优化。
五、PriorityBlockingQueue
public class PriorityBlockingQueueQuiz{
public static void main(String[] args) throw Exception {
BlockingQueue<Integer> queue = new PriorityBlockingQueue<>(2);
// 1. PriorityBlockingQueue put(Object) 方法不阻塞,不抛异常
// 2. PriorityBlockingQueue offer(Object) 方法不限制,允许长度变长
// 3. PriorityBlockingQueue 插入对象会做排序,默认参照元素 Comparable 实现,
// 或者显示地传递 Comparator
queue.put(9);
queue.put(1);
queue.put(8);
System.out.println("queue.size() =" + queue.size());
System.out.println("queue.take() =" + queue.take());
System.out.println("queue =" + queue);
}
}
/**
queue.size() =3
queue.take() =1
queue =[8, 9]
*/
六、SynchronusQueue
public class SynchronusQueueQuiz{
public static void main(String[] args) throws Exception {
BlockingQueue<Integer> queue = new SynchronousQueue<>();
// 1. SynchronousQueue 是无空间,offer 永远返回 false
// 2. SynchronousQueue take() 方法会被阻塞,必须被其他线程显示地调用 put(Object);
System.out.pringln("queue.offer(1) = " + queue.offer(1));
System.out.pringln("queue.offer(2) = " + queue.offer(2));
System.out.pringln("queue.offer(3) = " + queue.offer(3));
System.out.println("queue.take() = " + queue.take());
System.out.println("queue.size = " + queue.size());
}
}
七、BlockingQueue offer()
请评估以下程序的运行结果?
public class BlockingQueueQuiz{
public static void main(String[] args) throws Exception {
offer(new ArrayBlockingQueue<>(2));
offer(new LinkedBlockingQueue<>(2));
offer(new PriorityBlockingQueue<>(2));
offer(new SynchronousQueue<>());
}
}
private static void offer(BlockingQueue<Integer> queue) throws Exception {
System.out.println("queue.getClass() = " +queue.getClass().getName());
System.out.println("queue.offer(1) = " + queue.offer(1));
System.out.println("queue.offer(2) = " + queue.offer(2));
System.out.println("queue.offer(3) = " + queue.offer(3));
System.out.println("queue.size() = " + queue.size());
System.out.println("queue.take() = " + queue.take());
}
}
Java
并发框架
一、锁Lock
锁主要提供了两种特性:互斥性和不可见性
请说明ReentranLock
与ReentrantReadWriteLock
的区别?
jdk 1.5
以后,ReentranLock
(重进入锁)与ReentrantReadWriteLock
都是可重进入的锁,ReentranLock
都是互斥的,而ReentrantReadWriteLock
是共享的,其中里面有两个类,一个是 ReadLock
(共享,并行,强调数据一致性或者说可见性),另一个是 WriteLock
(互斥,串行)。
请解释ReentrantLock
为什么命名为重进入?
public class ReentrantLockQuestion {
/**
* T1 , T2 , T3
*
* T1(lock) , T2(park), T3(park)
* Waited Queue -> Head-> T2 next -> T3
* T1(unlock) -> unpark all
* Waited Queue -> Head-> T2 next -> T3
* T2(free), T3(free)
*
* -> T2(lock) , T3(park)
* Waited Queue -> Head-> T3
* T2(unlock) -> unpark all
* T3(free)
*/
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
// thread[main] ->
// lock lock lock
// main -> action1() -> action2() -> action3()
synchronizedAction(ReentrantLockQuestion::action1);
}
private static void action1() {
synchronizedAction(ReentrantLockQuestion::action2);
}
private static void action2() {
synchronizedAction(ReentrantLockQuestion::action3);
}
private static void action3() {
System.out.println("Hello,World");
}
private static void synchronizedAction(Runnable runnable) {
lock.lock();
try {
runnable.run();
} finally {
lock.unlock();
}
}
}
请说明 Lock#lock() 与 Lock#lockInterruptibly() 的区别?
/**
* java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued
* 如果当前线程已被其他线程调用了 interrupt() 方法时,这时会返回 true
* acquireQueued 执行完时,interrupt 清空(false)
* 再通过 selfInterrupt() 方法将状态恢复(interrupt=true)
*/
public static void main(String[] args) {
lockVsLockInterruptibly();
}
private static void lockVsLockInterruptibly() {
try {
lock.lockInterruptibly();
action1();
} catch (InterruptedException e) {
// 显示地恢复中断状态
Thread.currentThread().interrupt();
// 当前线程并未消亡,线程池可能还在存活
} finally {
lock.unlock();
}
}
lock() 优先考虑获取锁,待获取锁成功后,才响应中断。
**lockInterruptibly() ** 优先考虑响应中断,而不是响应锁的普通获取或重入获取。
ReentrantLock.lockInterruptibly 允许在等待时由其它线程调用等待线程的 Thread.interrupt 方法来中断等待线程的等待而直接返回,这时不用获取锁,而会抛出一个 InterruptedException。
ReentrantLock.lock 方法不允许 Thread.interrupt 中断,即使检测到 Thread.isInterrupted ,一样会继续尝试获取锁,失败则继续休眠。只是在最后获取锁成功后再把当前线程置为 interrupted 状态,然后再中断线程。
二、条件变量
Condition
使用场景
CountDownLatch(condition变种)
CyclicBarrier
(循环屏障)- 信号量/灯
(Semaphore)java9
- 生产者和消费者
- 阻塞队列
请解释 Condition await() 和 signal() 与 Object wait () 和 notify() 的相同与差异?
相同:阻塞和释放
差异:Java Thread 对象和实际 JVM 执行的 OS Thread 不是相同对象,JVM Thread 回调 Java Thread.run() 方法,同时 Thread 提供一些 native 方法来获取 JVM Thread 状态,当JVM thread 执行后,自动 notify()了。
三、屏障Barriers
请说明 CountDownLatch 与 CyclicBarrier 的区别?
CountDownLatch : 不可循环的,一次性操作(倒计时)。
public class CountDownLatchQuestion {
public static void main(String[] args) throws InterruptedException {
// 倒数计数 5
CountDownLatch latch = new CountDownLatch(5);
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 4; i++) {
executorService.submit(() -> {
action();
latch.countDown(); // -1
});
}
// 等待完成
// 当计数 > 0,会被阻塞
latch.await();
System.out.println("Done");
// 关闭线程池
executorService.shutdown();
}
private static void action() {
System.out.printf("线程[%s] 正在执行...\n", Thread.currentThread().getName()); // 2
}
}
CyclicBarrier:可循环的, 先计数 -1,再判断当计数 > 0 时候,才阻塞。
public class CyclicBarrierQuestion {
public static void main(String[] args) throws InterruptedException {
CyclicBarrier barrier = new CyclicBarrier(5); // 5
ExecutorService executorService = Executors.newFixedThreadPool(5); // 3
for (int i = 0; i < 20; i++) {
executorService.submit(() -> {
action();
try {
// CyclicBarrier.await() = CountDownLatch.countDown() + await()
// 先计数 -1,再判断当计数 > 0 时候,才阻塞
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
});
}
// 尽可能不要执行完成再 reset
// 先等待 3 ms
executorService.awaitTermination(3, TimeUnit.MILLISECONDS);
// 再执行 CyclicBarrier reset
// reset 方法是一个废操作
barrier.reset();
System.out.println("Done");
// 关闭线程池
executorService.shutdown();
}
private static void action() {
System.out.printf("线程[%s] 正在执行...\n", Thread.currentThread().getName()); // 2
}
}
请说明 Semaphore(信号量/灯) 的使用场景?
Semaphore 和Lock类似,比Lock灵活。其中有 acquire() 和 release() 两种方法,arg 都等于 1。acquire() 会抛出 InterruptedException,同时从 sync.acquireSharedInterruptibly(arg:1)
可以看出是读模式(shared); release()中可以计数,可以控制数量,permits可以传递N个数量。
public class SemaphoreTest {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 5; i++) {
new SecurityCheckThread(i, semaphore).start();
}
}
private static class SecurityCheckThread extends Thread{
private int seq;
private Semaphore semaphore;
public SecurityCheckThread(int seq, Semaphore semaphore){
this.seq = seq;
this.semaphore = semaphore;
}
@Override
public void run() {
try {
semaphore.acquire();
System.out.println("No." + seq + "乘客,正在查验中....");
if (seq%2 == 0){
Thread.sleep(1000);
System.out.println("No." + seq + "乘客,身份可疑,不能出国!");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
System.out.println("No." + seq + "乘客已完成服务。");
}
}
}
}
CountDownLatch
实现
public class LegacyCountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
// 倒数计数 5
MyCountDownLatch latch = new MyCountDownLatch(5);
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
executorService.submit(() -> {
action();
latch.countDown(); // -1
});
}
// 等待完成
// 当计数 > 0,会被阻塞
latch.await();
System.out.println("Done");
// 关闭线程池
executorService.shutdown();
}
private static void action() {
System.out.printf("线程[%s] 正在执行...\n", Thread.currentThread().getName()); // 2
}
/**
* Java 1.5+ Lock 实现
*/
private static class MyCountDownLatch {
private int count;
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private MyCountDownLatch(int count) {
this.count = count;
}
public void await() throws InterruptedException {
// 当 count > 0 等待
if (Thread.interrupted()) {
throw new InterruptedException();
}
lock.lock();
try {
while (count > 0) {
condition.await(); // 阻塞当前线程
}
} finally {
lock.unlock();
}
}
public void countDown() {
lock.lock();
try {
if (count < 1) {
return;
}
count--;
if (count < 1) { // 当数量减少至0时,唤起被阻塞的线程
condition.signalAll();
}
} finally {
lock.unlock();
}
}
}
/**
* Java < 1.5 实现
*/
private static class LegacyCountDownLatch {
private int count;
private LegacyCountDownLatch(int count) {
this.count = count;
}
public void await() throws InterruptedException {
// 当 count > 0 等待
if (Thread.interrupted()) {
throw new InterruptedException();
}
synchronized (this) {
while (count > 0) {
wait(); // 阻塞当前线程
}
}
}
public void countDown() {
synchronized (this) {
if (count < 1) {
return;
}
count--;
if (count < 1) { // 当数量减少至0时,唤起被阻塞的线程
notifyAll();
}
}
}
}
}
四、线程池
请问J.U.C
中内建了几种ExceptionService
实现?
1.5:ThreadPoolExecutor、ScheduledThreadPoolExecutor
1.7:ForkJoinPool
public class ExecutorServiceQuestion {
public static void main(String[] args) {
/**
* 1.5
* ThreadPoolExecutor
* ScheduledThreadPoolExecutor :: ThreadPoolExecutor
* 1.7
* ForkJoinPool
*/
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService = Executors.newScheduledThreadPool(2);
// executorService 不再被引用,它会被 GC -> finalize() -> shutdown()
ExecutorService executorService2 = Executors.newSingleThreadExecutor();
}
}
如何获取 ThreadPoolExecutor 正在运行的线程?
public class ThreadPoolExecutorThreadQuestion {
public static void main(String[] args) throws InterruptedException {
// main 线程启动子线程,子线程的创造来自于 Executors.defaultThreadFactory()
ExecutorService executorService = Executors.newCachedThreadPool();
// 之前了解 ThreadPoolExecutor beforeExecute 和 afterExecute 能够获取当前线程数量
Set<Thread> threadsContainer = new HashSet<>();
setThreadFactory(executorService, threadsContainer);
for (int i = 0; i < 9; i++) { // 开启 9 个线程
executorService.submit(() -> {
});
}
// 线程池等待执行 3 ms
executorService.awaitTermination(3, TimeUnit.MILLISECONDS);
threadsContainer.stream()
.filter(Thread::isAlive)
.forEach(thread -> {
System.out.println("线程池创造的线程 : " + thread);
});
Thread mainThread = Thread.currentThread();
ThreadGroup mainThreadGroup = mainThread.getThreadGroup();
int count = mainThreadGroup.activeCount();
Thread[] threads = new Thread[count];
mainThreadGroup.enumerate(threads, true);
Stream.of(threads)
.filter(Thread::isAlive)
.forEach(thread -> {
System.out.println("线程 : " + thread);
});
// 关闭线程池
executorService.shutdown();
}
private static void setThreadFactory(ExecutorService executorService, Set<Thread> threadsContainer) {
if (executorService instanceof ThreadPoolExecutor) {
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executorService;
ThreadFactory oldThreadFactory = threadPoolExecutor.getThreadFactory();
threadPoolExecutor.setThreadFactory(new DelegatingThreadFactory(oldThreadFactory, threadsContainer));
}
}
private static class DelegatingThreadFactory implements ThreadFactory {
private final ThreadFactory delegate;
private final Set<Thread> threadsContainer;
private DelegatingThreadFactory(ThreadFactory delegate, Set<Thread> threadsContainer) {
this.delegate = delegate;
this.threadsContainer = threadsContainer;
}
@Override
public Thread newThread(Runnable r) {
Thread thread = delegate.newThread(r);
// cache thread
threadsContainer.add(thread);
return thread;
}
}
}
五、Future
如何获取 Future 对象?
submit()
请举例 Future get() 以及 get(Long,TimeUnit) 方法的使用场景?
- 超时等待
InterruptedException
ExcutionException
TimeOutException
如何利用 Future 优雅地取消一个任务的执行?
public class CancellableFutureQuestion {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future future = executorService.submit(() -> { // 3秒内执行完成,才算正常
action(5);
});
try {
future.get(3, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// Thread 恢复中断状态
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
throw new RuntimeException(e);
} catch (TimeoutException e) {
// 执行超时,适当地关闭
Thread.currentThread().interrupt(); // 设置中断状态
future.cancel(true); // 尝试取消
}
executorService.shutdown();
}
private static void action(int seconds) {
try {
Thread.sleep(TimeUnit.SECONDS.toMillis(seconds)); // 5 - 3
// seconds - timeout = 剩余执行时间
if (Thread.interrupted()) { // 判断并且清除中断状态
return;
}
action();
} catch (InterruptedException e) {
}
}
private static void action() {
System.out.printf("线程[%s] 正在执行...\n", Thread.currentThread().getName()); // 2
}
}
六、Volatile
在 Java 中,volatile 保证的是可见性还是原子性?
volatile 既有可见性又有原子性(非我及彼),可见性是一定的,原子性是看情况的。对象类型和原生类型都是可见性,原生类型是原子性。
在 Java 中,volatile long 和 double 是线程安全的吗?
volatile long 和 double 是线程安全的。
在 Java 中,volatile 底层实现是基于什么机制?
内存屏障(变量 Lock)机制:一个变量的原子性的保证。
七、原子操作Atomic
为什么 AtomicBoolean 内部变量使用 int 实现,而非 boolean?
操作系统有 X86 和 X64,在虚拟机中,基于boolean 实现就是用 int 实现的,用哪一种实现都可以。虚拟机只有32位和64位的,所以用32位的实现。
在变量原子操作时,Atomic CAS 操作比 synchronized 关键字哪个更重?*
同线程的时候,synchronized 更快;而多线程的时候则要分情况讨论。
public class AtomicQuestion {
private static int actualValue = 3;
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(3);
// if( value == 3 )
// value = 5
atomicInteger.compareAndSet(3, 5);
// 偏向锁 < CAS 操作 < 重锁(完全互斥)
// CAS 操作也是相对重的操作,它也是实现 synchronized 瘦锁(thin lock)的关键
// 偏向锁就是避免 CAS(Compare And Set/Swap)操作
}
private synchronized static void compareAndSet(int expectedValue, int newValue) {
if (actualValue == expectedValue) {
actualValue = newValue;
}
}
}
Atomic CAS 的底层是如何实现?*
汇编指令:cpmxchg
(Compare and Exchange)