基础的线程Thread类,Runnable接口以及高级一点的Future,Callable接口。到后来的线程池ThreadPoolExecutor类,以及一些常用的volatile,synchronized关键字,原子类,通信工具类,还有一些并发集合
ConcurrentHashMap
,CopyOnWriteArrayList
,CopyOnWriteArraySet
,ConcurrentLinkedQueue
,BlockingQueue等等内容。今天博主为大家好好梳理一下这些内容,好啦开始吧。
基础类主要用于创建线程,比如Thread,Runnable,Future,Callable都可以实现。
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread is running");
}
}
public class Demo {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start(); // 注意:必须用 start() 启动线程
}
}
不过存在一个缺点,就是Java是不支持多继承的,所以继承了Thread类,就不可以继承其他类了,可扩展性有限。
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Runnable is running");
}
}
public class Demo {
public static void main(String[] args) {
MyRunnable r = new MyRunnable();
Thread t1 = new Thread(r);
t1.start();
}
}
但是存在一个问题就是Runnable不支持返回值
run()
方法不能抛出受检异常(checked exception)。如果任务中可能抛出异常,必须在方法内部捕获并处理。
ExecutorService executor = Executors.newFixedThreadPool(1);//就是一个固定大小的线程池
Future future = executor.submit(() -> {
Thread.sleep(1000);
return 42;
});
System.out.println("Doing other things...");
System.out.println("Result: " + future.get()); // 阻塞直到拿到结果
executor.shutdown();
class MyCallable implements Callable {
@Override
public Integer call() throws Exception {
Thread.sleep(1000);
return 100;
}
}
public class Demo {
public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newSingleThreadExecutor();//不会并发执行,线程池中只有一个线程
Future future = executor.submit(new MyCallable());
System.out.println("Result: " + future.get());
executor.shutdown();
}
}
ThreadPoolExecutor(面试重点)
首先说说咋用,得知道咋设置参数吧。有七大参数。
corePoolSize
核心线程数 CPU密集型:一般设置为CPU核数(+1)IO密集型: 一般为CPU核数的2倍
含义:线程池中一直保留的线程数量,即使它们是空闲的,也不会被回收(除非设置了特殊参数)。
作用:提前准备好一定数量的线程,处理常规负载。
maximumPoolSize
最大线程数含义:线程池中允许存在的最大线程数量。
作用:当任务量激增时,可以临时创建更多线程,但不超过这个最大值。
keepAliveTime
线程空闲存活时间含义:当线程数量超过核心线程数时,多余的线程在空闲多久后会被销毁。
作用:防止临时增加的线程无限制地存在,浪费资源。
举例:keepAliveTime = 60
秒,说明额外的线程空闲超过60秒就会被回收。
unit
时间单位含义:配合 keepAliveTime
使用,指定时间的单位。
常用值:TimeUnit.SECONDS
(秒)、TimeUnit.MILLISECONDS
(毫秒)、TimeUnit.MINUTES
(分钟)等。
workQueue
任务队列含义:用于缓存等待执行的任务。
作用:当核心线程满了,新的任务就会放进这个队列排队。
常见实现:
ArrayBlockingQueue
:有界队列,固定大小,常用。
LinkedBlockingQueue
:无界队列(实际上是很大),任务堆积多时容易OOM。
SynchronousQueue
:不存储元素,提交任务必须直接交给线程处理(用于任务数量多、处理快的场景)。
常见的阻塞队列
public class ArrayBlockingQueue extends AbstractQueue
implements BlockingQueue, java.io.Serializable {
private final Object[] items;
private int takeIndex; // 取出元素的索引
private int putIndex; // 插入元素的索引
private int count; // 当前队列中元素的数量
private final ReentrantLock lock = new ReentrantLock(); // 锁
private final Condition notEmpty = lock.newCondition(); // 空队列条件
private final Condition notFull = lock.newCondition(); // 满队列条件
// 构造方法,指定容量
public ArrayBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.items = new Object[capacity];
}
@Override
public void put(E e) throws InterruptedException {
// 等待直到队列不满
final ReentrantLock lock = this.lock;
lock.lock();
try {
while (count == items.length)
notFull.await();
enqueue(e);
} finally {
lock.unlock();
}
}
private void enqueue(E e) {
items[putIndex] = e;
if (++putIndex == items.length) putIndex = 0;
++count;
notEmpty.signal();
}
@Override
public E take() throws InterruptedException {
// 等待直到队列不空
final ReentrantLock lock = this.lock;
lock.lock();
try {
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}
private E dequeue() {
@SuppressWarnings("unchecked")
E x = (E) items[takeIndex];
items[takeIndex] = null;
if (++takeIndex == items.length) takeIndex = 0;
--count;
notFull.signal();
return x;
}
// 其他方法略...
}
public class LinkedBlockingQueue extends AbstractQueue
implements BlockingQueue, java.io.Serializable {
private final Node head; // 队列头
private Node last; // 队列尾部
private final ReentrantLock lock = new ReentrantLock(); // 锁
private final Condition notEmpty = lock.newCondition(); // 空队列条件
private final Condition notFull = lock.newCondition(); // 满队列条件
private int count; // 当前队列中元素的数量
private final int capacity; // 队列最大容量
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
this.head = new Node(null);
this.last = head;
}
@Override
public void put(E e) throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
while (count == capacity)
notFull.await();
enqueue(e);
} finally {
lock.unlock();
}
}
private void enqueue(E e) {
Node node = new Node(e);
last.next = node;
last = node;
++count;
notEmpty.signal();
}
@Override
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}
private E dequeue() {
Node first = head.next;
head.next = first.next;
if (first == last)
last = head;
--count;
notFull.signal();
return first.item;
}
// 其他方法略...
}
public class PriorityBlockingQueue extends AbstractQueue
implements BlockingQueue, java.io.Serializable {
private final Comparator super E> comparator; // 优先级比较器
private final PriorityQueue queue;
public PriorityBlockingQueue(int initialCapacity, Comparator super E> comparator) {
this.queue = new PriorityQueue(initialCapacity, comparator);
this.comparator = comparator;
}
@Override
public void put(E e) throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
queue.add(e); // 使用PriorityQueue的add方法按优先级插入
} finally {
lock.unlock();
}
}
@Override
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (queue.isEmpty()) {
return null; // 队列为空返回null
}
return queue.poll(); // 按优先级取出元素
} finally {
lock.unlock();
}
}
// 其他方法略...
}
package java.util.concurrent;
import java.util.*;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class DelayQueue extends AbstractQueue
implements BlockingQueue {
private final transient ReentrantLock lock = new ReentrantLock();
private final PriorityQueue q = new PriorityQueue<>();
private Thread leader = null;
private final Condition available = lock.newCondition();
public DelayQueue() {}
public DelayQueue(Collection extends E> c) {
this.addAll(c);
}
@Override
public boolean add(E e) {
return offer(e);
}
@Override
public boolean offer(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
q.offer(e);
if (q.peek() == e) {
leader = null;
available.signal();
}
return true;
} finally {
lock.unlock();
}
}
@Override
public void put(E e) {
offer(e);
}
@Override
public boolean offer(E e, long timeout, TimeUnit unit) {
return offer(e);
}
@Override
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
E first = q.peek();
if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
return null;
else
return q.poll();
} finally {
lock.unlock();
}
}
@Override
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
E first = q.peek();
if (first == null)
available.await();
else {
long delay = first.getDelay(TimeUnit.NANOSECONDS);
if (delay <= 0)
return q.poll();
first = null; // 不要保留引用
if (leader != null)
available.await();
else {
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
available.awaitNanos(delay);
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
if (leader == null && q.peek() != null)
available.signal();
lock.unlock();
}
}
@Override
public E peek() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return q.peek();
} finally {
lock.unlock();
}
}
@Override
public int size() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return q.size();
} finally {
lock.unlock();
}
}
@Override
public int drainTo(Collection super E> c) {
return drainTo(c, Integer.MAX_VALUE);
}
@Override
public int drainTo(Collection super E> c, int maxElements) {
if (c == null)
throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
if (maxElements <= 0)
return 0;
final ReentrantLock lock = this.lock;
lock.lock();
try {
int n = 0;
while (n < maxElements) {
E first = q.peek();
if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
break;
c.add(q.poll());
++n;
}
return n;
} finally {
lock.unlock();
}
}
@Override
public Iterator iterator() {
return new Itr(toArray());
}
private Object[] toArray() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return q.toArray();
} finally {
lock.unlock();
}
}
private class Itr implements Iterator {
final Object[] array; // Array of all elements
int cursor; // Index of next element to return
int lastRet; // Index of last element, or -1 if none
Itr(Object[] array) {
this.array = array;
lastRet = -1;
}
@Override
public boolean hasNext() {
return cursor < array.length;
}
@Override
@SuppressWarnings("unchecked")
public E next() {
if (cursor >= array.length)
throw new NoSuchElementException();
lastRet = cursor;
return (E) array[cursor++];
}
@Override
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
Object x = array[lastRet];
lastRet = -1;
final ReentrantLock lock = DelayQueue.this.lock;
lock.lock();
try {
for (Iterator it = q.iterator(); it.hasNext(); ) {
if (it.next() == x) {
it.remove();
break;
}
}
} finally {
lock.unlock();
}
}
}
}
package java.util.concurrent;
import java.util.*;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class DelayQueue extends AbstractQueue
implements BlockingQueue {
private final transient ReentrantLock lock = new ReentrantLock();
private final PriorityQueue q = new PriorityQueue<>();
private Thread leader = null;
private final Condition available = lock.newCondition();
public DelayQueue() {}
public DelayQueue(Collection extends E> c) {
this.addAll(c);
}
@Override
public boolean add(E e) {
return offer(e);
}
@Override
public boolean offer(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
q.offer(e);
if (q.peek() == e) {
leader = null;
available.signal();
}
return true;
} finally {
lock.unlock();
}
}
@Override
public void put(E e) {
offer(e);
}
@Override
public boolean offer(E e, long timeout, TimeUnit unit) {
return offer(e);
}
@Override
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
E first = q.peek();
if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
return null;
else
return q.poll();
} finally {
lock.unlock();
}
}
@Override
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
E first = q.peek();
if (first == null)
available.await();
else {
long delay = first.getDelay(TimeUnit.NANOSECONDS);
if (delay <= 0)
return q.poll();
first = null; // 不要保留引用
if (leader != null)
available.await();
else {
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
available.awaitNanos(delay);
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
if (leader == null && q.peek() != null)
available.signal();
lock.unlock();
}
}
@Override
public E peek() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return q.peek();
} finally {
lock.unlock();
}
}
@Override
public int size() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return q.size();
} finally {
lock.unlock();
}
}
@Override
public int drainTo(Collection super E> c) {
return drainTo(c, Integer.MAX_VALUE);
}
@Override
public int drainTo(Collection super E> c, int maxElements) {
if (c == null)
throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
if (maxElements <= 0)
return 0;
final ReentrantLock lock = this.lock;
lock.lock();
try {
int n = 0;
while (n < maxElements) {
E first = q.peek();
if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
break;
c.add(q.poll());
++n;
}
return n;
} finally {
lock.unlock();
}
}
@Override
public Iterator iterator() {
return new Itr(toArray());
}
private Object[] toArray() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return q.toArray();
} finally {
lock.unlock();
}
}
private class Itr implements Iterator {
final Object[] array; // Array of all elements
int cursor; // Index of next element to return
int lastRet; // Index of last element, or -1 if none
Itr(Object[] array) {
this.array = array;
lastRet = -1;
}
@Override
public boolean hasNext() {
return cursor < array.length;
}
@Override
@SuppressWarnings("unchecked")
public E next() {
if (cursor >= array.length)
throw new NoSuchElementException();
lastRet = cursor;
return (E) array[cursor++];
}
@Override
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
Object x = array[lastRet];
lastRet = -1;
final ReentrantLock lock = DelayQueue.this.lock;
lock.lock();
try {
for (Iterator it = q.iterator(); it.hasNext(); ) {
if (it.next() == x) {
it.remove();
break;
}
}
} finally {
lock.unlock();
}
}
}
}
threadFactory
线程工厂含义:用来定制线程的创建方式,比如给线程起名字、设置优先级、是否守护线程等。
作用:让线程更容易管理和排查,比如异常时能快速定位是哪条线程。
默认实现:Executors.defaultThreadFactory()
。
举例:可以自己实现一个 ThreadFactory
,给线程设置统一的前缀名字。
handler
拒绝策略含义:当线程池满了(线程数=最大线程数,且任务队列也满了),新任务怎么办。
常见策略:
AbortPolicy
(默认):直接抛异常(RejectedExecutionException
)。
CallerRunsPolicy
:交给提交任务的线程自己执行(让主线程去跑任务)。
DiscardPolicy
:直接丢弃任务,不抛异常。
DiscardOldestPolicy
:丢掉最老的任务(队列头部),然后尝试重新提交新任务。
import java.util.concurrent.*;
public class CustomThreadPoolExample {
public static void main(String[] args) {
// 自定义线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 核心线程数
4, // 最大线程数
60, // 空闲线程存活时间
TimeUnit.SECONDS, // 时间单位
new LinkedBlockingQueue<>(2) // 任务队列容量为 2
);
// 提交任务
for (int i = 1; i <= 6; i++) {
int taskId = i;
executor.submit(() -> {
System.out.println("Task " + taskId + " is running on thread " + Thread.currentThread().getName());
try {
Thread.sleep(1000); // 模拟任务耗时
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
// 关闭线程池
executor.shutdown();
}
}
newFixedThreadPool()固定大小 数据库连接池
newCachedThreadPool()缓存线程池 短时间内存在大量文件处理或网络请求
newScheduledThreadPool()定时任务 定时发送邮件,定时备份数据
newSingleThreadExecutor()单线程池 日志记录 文件处理
try-catch
异常处理try {
int result = 10 / 0; // 会抛出ArithmeticException
} catch (ArithmeticException e) {
System.out.println("发生了除零错误: " + e.getMessage());
} finally {
System.out.println("无论如何都会执行");
}
Future
接口Future
是 Java 中的一个接口,代表一个异步计算的结果。它通常用于表示任务的结果,并提供方法来检查任务的状态、取消任务或获取任务的执行结果。
get()
:获取任务的结果,若任务未完成则会阻塞。
cancel()
:尝试取消任务的执行。
isDone()
:检查任务是否完成。
isCancelled()
:检查任务是否被取消。
ExecutorService executor = Executors.newFixedThreadPool(1);
Future future = executor.submit(() -> {
// 模拟耗时操作
Thread.sleep(1000);
return 42;
});
try {
Integer result = future.get(); // 阻塞,直到任务完成
System.out.println("任务的结果是: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
executor.shutdown();
}
注意:调用 get()
会阻塞,直到 Future
对象代表的任务完成。
afterExecute
方法afterExecute
是 ThreadPoolExecutor
类的一个方法,可以在任务执行完成后执行一些操作。它通常用于日志记录、清理资源等操作。
class MyThreadPoolExecutor extends ThreadPoolExecutor {
public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t != null) {
System.out.println("任务执行时发生了异常: " + t.getMessage());
} else {
System.out.println("任务执行成功!");
}
}
}
public class Example {
public static void main(String[] args) {
MyThreadPoolExecutor executor = new MyThreadPoolExecutor(
1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
executor.submit(() -> {
// 执行的任务
System.out.println("任务开始执行");
});
executor.shutdown();
}
}
在上面的代码中,afterExecute
会在每个任务执行结束后被调用,用于处理任务执行后的操作,如异常处理或日志记录。
UncaughtExceptionHandler
捕获异常UncaughtExceptionHandler
是 Java 中的一个接口,用于处理线程未捕获的异常。当线程中的异常没有被捕获时,UncaughtExceptionHandler
可以提供全局处理机制。
UncaughtExceptionHandler
:你可以为每个线程设置一个 UncaughtExceptionHandler
,或者使用全局的默认处理器。
public class Example {
public static void main(String[] args) {
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
System.out.println("捕获到未处理的异常: " + throwable.getMessage());
});
Thread thread = new Thread(() -> {
// 模拟抛出异常
throw new RuntimeException("任务执行失败");
});
thread.start();
}
}
在上面的代码中,Thread.setDefaultUncaughtExceptionHandler()
会设置一个全局的未捕获异常处理器,捕获到任何线程中的未处理异常时,都会触发该处理器。
你也可以为每个线程单独设置 UncaughtExceptionHandler
,如下所示:
Thread thread = new Thread(() -> {
// 模拟抛出异常
throw new RuntimeException("任务执行失败");
});
thread.setUncaughtExceptionHandler((t, e) -> {
System.out.println("捕获到线程 " + t.getName() + " 的异常: " + e.getMessage());
});
thread.start();
该处理器会在线程抛出未捕获异常时执行,用于日志记录、清理资源等。
try-catch
用于捕获和处理异常。
Future
用于获取异步任务的执行结果,提供了一些方法来检查任务的状态、获取结果、取消任务。
afterExecute
是 ThreadPoolExecutor
中的一个方法,可以在任务完成后执行某些操作,比如日志记录、清理资源等。
UncaughtExceptionHandler
用于处理未捕获的线程异常,可以通过设置全局或线程级的异常处理器来捕获和处理这些异常。
线程池的状态:Runnable--->SHUTDOWN---->STOP---->TIDYING---->TERMINATED
volatile
volatile
是一个轻量级的同步机制,用于确保变量的可见性和禁止指令重排序。可见性:
当一个线程修改了volatile
修饰的变量时,其他线程可以立即看到最新的值。这是因为 volatile
变量会直接从主内存中读取,而不是从线程的本地缓存中读取。禁止指令重排序:
JVM 在优化代码时可能会对指令进行重排序,而volatile
能够防止这种重排序,从而保证程序的执行顺序符合预期。不保证原子性:
volatile
不能保证复合操作(如 i++
)的原子性。如果需要原子性操作,可以配合 synchronized
或使用 Atomic
类。volatile
标志位控制线程运行。class VolatileExample {
private volatile boolean running = true;
public void stop() {
running = false;
}
public void run() {
while (running) {
// 执行任务
}
System.out.println("Thread stopped");
}
}
synchronized
更轻量级,性能开销小。synchronized(博主在前一篇文章详细讨论了Java中的锁,感兴趣的可以去看看)
synchronized
是一种重量级的同步机制,用于确保同一时刻只有一个线程可以访问某个资源。互斥性:
同一时刻只有一个线程可以持有锁,其他线程必须等待锁释放后才能进入。可见性:
线程在进入synchronized
块时会刷新本地缓存,退出时会将修改后的值写回主内存,从而保证变量的可见性。原子性:
synchronized
能够保证代码块或方法中的操作是原子性的,避免多线程并发导致的数据不一致问题。可重入性:
同一个线程可以多次获取同一个锁,而不会发生死锁。synchronized
修饰方法,锁住整个方法。public synchronized void increment() {
count++;
}
synchronized
锁定特定的对象或代码块。public void increment() {
synchronized (this) {
count++;
}
}
i++
)。class BankAccount {
private int balance;
public synchronized void deposit(int amount) {
balance += amount;
}
public synchronized void withdraw(int amount) {
if (balance >= amount) {
balance -= amount;
} else {
System.out.println("Insufficient balance");
}
}
}
volatile
和 synchronized
的对比特性 | volatile |
synchronized |
---|---|---|
作用范围 | 单个变量 | 方法或代码块 |
可见性 | 保证变量的可见性 | 保证变量的可见性 |
原子性 | 不保证复合操作的原子性 | 保证操作的原子性 |
互斥性 | 不提供互斥锁 | 提供互斥锁 |
性能开销 | 较低 | 较高 |
适用场景 | 简单的状态标志或共享变量 | 复杂的同步逻辑或临界区保护 |
这里重点介绍ConcurrentHashMap,是HashMap的线程安全版本。
对于读操作:通过volatile实现线程间内存变量的可见性
对于写操作:JDK7前通过分段所实现,JDK8后通过CAS+synchronized实现。
JDK7:对于完整的Map会被分为若干段,每个端都可以独立地加锁,内部维护一个HashEntry
JDK8:先尝试通过CAS加锁,失败后重试,重试超过一定过的次数,直接通过synchronized加锁。(仅在不要使加锁)
public void lock() {
int spins = 0;
// 尝试通过 CAS 获取锁
while (!state.compareAndSet(0, 1)) {
if (++spins > MAX_SPINS) {
// 如果自旋次数超过阈值,回退到 synchronized 加锁
synchronized (this) {
try {
// 确保锁被释放后再次尝试获取
while (state.get() != 0) {
this.wait(); // 等待锁释放
}
state.set(1); // 获取锁
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return;
}
}
}
}
public void unlock() {
if (state.get() == 1) {
state.set(0); // 释放锁
synchronized (this) {
this.notifyAll(); // 唤醒等待的线程
}
}
}
好啦这篇文章先写到这里,以后还会继续补充并发工具类以及其他内容的。如果由其他想看的内容也可以告诉博主哦。
感谢你看到这里,喜欢的话可以点点关注哦!