创建线程方式
- 继承Thread,重写run方法
class MyThread extends Thread {
@Override
public void run() {
//逻辑
}
}
// 实例化线程
MyThread myThread = new MyThread();
// 启动线程
myThread.start();
- 现实Runnable接口,重写run方法
class MyRunnable implements Runnable {
@Override
public void run() {
// 逻辑
}
}
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
- 创建未来任务对象,可获取返回值
// 创建未来任务对象
FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
// 逻辑
// 返回处理结果
return 1;
}
});
// 创建线程对象
Thread thread = new Thread(task);
thread.start();
// 获取未来任务线程返回值,此时需要等待未来任务完成才能获取返回值
Integer i = task.get();
- 线程池(下面详解)
// 示例
// 创建线程池对象(线程池中有3个线程)
ExecutorService executorService = Executors.newFixedThreadPool(3);
// 将任务交给线程池
executorService.submit(new Runnable() {
@Override
public void run() {
// 逻辑
}
});
// 关闭线程池
executorService.shutdown();
线程池详解
// 官方原生线程池创建,阿里巴巴建议使用此方式创建线程池,灵活可控制
try (ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
// 核心线程数量,在线程池内创建后不销毁,一直存在
2,
// 总线程数量 = 核心线程数 + 非核心线程数量
Integer.MAX_VALUE,
// 非线程保活时间
1,
// 非线程保活时间单位
TimeUnit.SECONDS,
// 线程缓存的队列方式
// 有界队列 可指定容量 数组
new ArrayBlockingQueue<>(2)
// 无界队列 链表
// new LinkedBlockingDeque<>(2)
// 优先任务队列,根据任务优先级顺序执行任务
// new PriorityBlockingQueue<>(2)
// 直接提交队列 不使用队列
// new SynchronousQueue<>()
)) {
// 声明 submit 提交方式的执行结果集合
HashSet<Future<Boolean>> submitFutures = new HashSet<>();
for (int i = 0; i < 10; i++) {
// 执行线程任务 只能执行Runnable接口对象 无返回结果
threadPoolExecutor.execute(() -> {
});
// 执行线程任务 可以执行 Callable 和 Runnable接口对象,底层执行的execute()方法
// submit 方法执行后返回Future 接口
Future<Boolean> submitResult = threadPoolExecutor.submit(() -> true);
// 等待线程执行完毕
submitResult.get();
}
// 关闭线程池 等待缓存队列完成,不再接受新任务
threadPoolExecutor.shutdown();
// 关闭线程池 丢弃缓存队列
threadPoolExecutor.shutdownNow();
}
// 快捷方式创建线程池
/**
* 创建固定数量线程池
* 声明固定的核心线程数量,无非核心数量
* return new ThreadPoolExecutor(nThreads, nThreads,
* 0L, TimeUnit.MILLISECONDS,
* new LinkedBlockingQueue<Runnable>());
*/
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.execute(() -> {
});
/**
* 创建调度线程池
* 声明固定的核心线程数量,其他默认
* return super(corePoolSize, Integer.MAX_VALUE,
* DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
* new DelayedWorkQueue());
*/
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
scheduledExecutorService.execute(() -> {
});
/**
* 创建缓存线程池
* 无核心线程,随使用创建,存活时间60S
* return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
* 60L, TimeUnit.SECONDS,
* new SynchronousQueue<Runnable>())
*/
ExecutorService executorService1 = Executors.newCachedThreadPool();
executorService1.execute(() -> {
});
/**
* 创建1个线程的线程池
* (new ThreadPoolExecutor(1, 1,
* 0L, TimeUnit.MILLISECONDS,
* new LinkedBlockingQueue<Runnable>(),
* threadFactory));
*/
ExecutorService executorService2 = Executors.newSingleThreadExecutor();
executorService2.execute(() -> {
});
线程池 - 等待线程执行完毕
// 线程池 CountDownLatch.await() 等待
// 不需要线程返回执行结果使用
public static void countDownLatchWait() throws Exception {
// 创建 CountDownLatch 线程等待计数器对象
CountDownLatch countDownLatch = new CountDownLatch(5);
// 创建线程池
try (ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 5, 1,
TimeUnit.SECONDS, new ArrayBlockingQueue<>(5))) {
for (int i = 0; i < 5; i++) {
int finalI = i;
// execute 和 submit 都可以使用
// threadPool.execute(() -> {
threadPool.submit(() -> {
try {
TimeUnit.SECONDS.sleep(finalI);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(finalI);
// 每个线程执行完毕 对countDownLatch-1操作
countDownLatch.countDown();
});
}
}
// 等待所有线程都执行完毕 countDownLatch计数器=0
countDownLatch.await();
System.out.println("执行完毕");
}
// 线程池 submit 提交后 使用 Future.get() 等待
// 需要线程返回执行结果使用
public static void submitFutureGetAwait() throws Exception {
// 声明 submit 提交方式的执行结果集合
ArrayList<Future<Boolean>> futures = new ArrayList<>();
// 创建线程池
try (ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 5, 1,
TimeUnit.SECONDS, new ArrayBlockingQueue<>(5))) {
for (int i = 0; i < 5; i++) {
int finalI = i;
Future<Boolean> submit = threadPool.submit(() -> {
TimeUnit.SECONDS.sleep(finalI);
System.out.println(finalI);
// 返回执行结果
return true;
});
// 将 submitResult 放入到集合中,在循环后统一调用
futures.add(submit);
}
}
// 获取线程执行结果,此时线程阻塞,需等全部线程执行完毕后才能继续往下执行
for (Future<Boolean> future : futures) {
// 获取执行结果
System.out.println(future.get());
}
// 运行到此处 线程已全部执行完毕
System.out.println("执行完毕");
}
线程详解
- 线程声明周期
1. 新建
2. 就绪
3. 运行
4. 超时等待
5. 等待
6. 阻塞
7. 终止
- 线程让位、线程休眠、设置守护线程、设置线程名称、设置线程优先级
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// 逻辑
// 获取当前线程
Thread currentThread = Thread.currentThread();
// 获取当前线程名称 t1
String name = currentThread.getName();
// 获取当前线程类加载器下的资源路径
String resource = Thread.currentThread().getContextClassLoader().getResource("").getPath();
try {
// 线程休眠,在声明的当前线程里起作用,单位毫秒
Thread.sleep(1000);
} catch (InterruptedException e) {
// 唤醒后会再此抛出异常
}
// 线程让位,会让出当前线程此次执行的时间片
Thread.yield();
}
});
// 设置线程名称
thread.setName("t1");
// java中线程属于抢占型cpu,设置线程优先级:1-10 默认5
thread.setPriority(Thread.MAX_PRIORITY);
// 进程分页用户线程和守护线程
// 开启守护线程,用于线程结束后,守护线程自动结束
thread.setDaemon(true);
// 启动线程
thread.start();
// 唤醒线程,线程的实例方法,利用抛出异常机制进行唤醒
thread.interrupt();
- 定时任务
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 声明第一次执行时间
Date firstDate = simpleDateFormat.parse("2025-1-1 09:00:00");
// 定时任务
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
// 逻辑
System.out.println("o");
}
},
firstDate,
// 执行第一次后,以后的执行间隔
1000);
- 线程合并
MyThread myThread = new MyThread();
myThread.start();
// 将myThread加入到此
myThread.join(1000);
// 等待myThread执行完成或时间到期后再往下执行
线程安全
线程安全同时出现:1.多线程 2.有共享数据 3.共享数据涉及修改
- 局部变量(在栈中,不是共享的)不存在线程安全问题。
- 实例变量和静态变量存在堆中,涉及到共享,存在线程安全问题。
public class Test {
public static void main(String[] args) throws Exception {
// 共享对象
User user = new User(1000);
// 线程1进行操作
new Thread(new Runnable() {
@Override
public void run() {
// 对象方法
user.withdraw(1000);
// 静态方法
// User.withdraw2(1000);
}
}).start();
// 线程2进行操作
new Thread(new Runnable() {
@Override
public void run() {
user.withdraw(1000);
// 静态方法
// User.withdraw2(1000);
}
}).start();
}
}
class User {
private int money;
public User(int money) {
this.money = money;
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
// 在共享对象的修改方法上或方法体内 使用synchronized关键字,进行多线程同步操作
// 共享对象方法
// public synchronized void withdraw(int monye) {
public void withdraw(int monye) {
// synchronized(共享对象){方法体}
synchronized (this) {
int cuMoney = this.getMoney();
try {
// 模拟卡顿
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 不开启同步,多个线程会同时到此,造成多次操作,造成多次成功
this.setMoney(cuMoney - monye);
}
System.out.println(this.getMoney());
}
// 共享静态方法
// public synchronized static void withdraw2(int money) {
public static void withdraw2(int money) {
// synchronized(共享静态类){方法体}
synchronized (User.class) {
// 逻辑
}
}
}
// 原子性操作 java.util.concurrent.atomic 包下的类
// 示例
AtomicInteger atomicInteger = new AtomicInteger(0);
// 线程计数器
CountDownLatch countDownLatch = new CountDownLatch(3);
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
atomicInteger.incrementAndGet();
}
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
countDownLatch.countDown();
});
Thread thread3 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
atomicInteger.incrementAndGet();
}
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
countDownLatch.countDown();
});
thread1.start();
thread3.start();
// thread1.join();
// thread3.join();
countDownLatch.await();
System.out.println(atomicInteger.get());
线程通信
- 需要在同步中操作
- 调用共享对象(父级object)的方法:wait()、notify()、notifyAll()
- 调用共享对象的obj.wait()后,在共享对象上活跃的所有线程进入无期限等待状态(当前线程会释放占用的对象锁,其他线程会获取共享对象锁进行执行,当前线程不唤醒就不会再去获取对象锁),直到在其他线程中调用了该共享对象的obj.notify()后进行唤醒(多个线程优先级优先,否则随机唤醒),唤醒后会接着上一次调用的wait()方法的位置继续向下执行。调用共享对象的obj.notifyAll()后唤醒所有在该共享对象上等待的线程。
可重入锁
使用Lock来实现线程安全
Lock接口下一个实现类:可重入锁ReentrantLock
让多线程共享一个Lock
Lock 比 synchronized 更加灵活,推荐使用
class User {
// 可重入锁 静态全局共享一个Lock
public static final ReentrantLock lock = new ReentrantLock();
public void withdraw(int money) {
// 加锁
lock.lock();
try{
// 同步逻辑
}finally {
// 解锁
lock.unlock();
}
}
}
虚拟线程: Java19引入
- 虚拟线程的核心概念
轻量级实现:虚拟线程由 JVM 管理,而非操作系统内核线程。每个虚拟线程初始仅占用几百字节内存,可轻松创建数百万个,显著降低资源消耗。
M:N 调度模型:虚拟线程通过协作式调度(Cooperative Scheduling)实现,多个虚拟线程(M)复用少量系统线程(N)。当虚拟线程遇到 I/O 阻塞时,JVM 会自动将其从载体线程(Carrier Thread)上卸载,释放资源以执行其他任务。
与平台线程的区别:
| 特性 | 传统线程(平台线程) | 虚拟线程 |
|---|---|---|
| 内存占用 | 每个线程数百 KB 到 MB | 初始仅几百字节,动态扩展 |
| 最大数量 | 受硬件限制(通常几千个) | 轻松支持数百万个 |
| 调度方式 | 操作系统抢占式调度 | JVM 协作式调度 |
| 适用场景 | CPU 密集型任务 | I/O 密集型、高并发场景 |
- 核心特性
- 低成本创建与销毁:虚拟线程的创建和销毁成本极低,无需线程池即可处理大量短期任务。
无阻塞 I/O 优化:虚拟线程在执行阻塞操作(如网络请求、文件读写)时,会主动释放载体线程,避免资源浪费。 - 编程模型兼容:虚拟线程与传统线程 API 高度兼容,开发者无需学习新语法,可直接使用 Thread、ExecutorService 等现有工具。
- 守护线程与优先级固定:虚拟线程始终为守护线程,且优先级无法通过 setPriority() 修改。
- 创建线程
- 创建虚拟线程:
Runnable task = () -> {
try {
Thread.sleep(Duration.ofSeconds(1));
System.out.println("Virtual Thread executed");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
};
Thread thread = Thread.ofVirtual().name("Virtual-Thread").start(task);
- 批量任务处理:
使用 Executors.newVirtualThreadPerTaskExecutor() 为每个任务分配独立虚拟线程:
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 10_000).forEach(i -> {
executor.submit(() -> {
Thread.sleep(Duration.ofMillis(100));
return i;
});
});
}