多线程基础概念
本文系统介绍了Java多线程编程的核心概念与技术。主要内容包括:线程的两种创建方式(继承Thread类和实现Runnable接口)、五个生命周期状态(新建、就绪、运行、阻塞、死亡)、同步机制(synchronized和Lock)、线程通信(wait/notify)、线程池使用及死锁问题。还详细讲解了并发工具类(CountDownLatch等)、线程安全集合、volatile关键字、原子类、Thre
多线程是指在一个进程中同时运行多个线程,每个线程执行不同的任务。线程是操作系统能够进行运算调度的最小单位,被包含在进程之中,是进程中的实际运作单位。
线程与进程的区别在于,进程是资源分配的最小单位,线程是CPU调度的最小单位。同一个进程中的多个线程共享进程的资源,包括内存、文件描述符等。
线程的创建方式
在Java中,创建线程有两种主要方式:继承Thread类和实现Runnable接口。继承Thread类需要重写run方法,实现Runnable接口需要实现run方法。
// 继承Thread类
class MyThread extends Thread {
public void run() {
System.out.println("Thread running");
}
}
// 实现Runnable接口
class MyRunnable implements Runnable {
public void run() {
System.out.println("Runnable running");
}
}
线程的生命周期
线程的生命周期包括新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)五个状态。新建状态是线程被创建但未启动;就绪状态是线程已启动但未获得CPU时间片;运行状态是线程正在执行;阻塞状态是线程因等待资源或I/O操作而暂停执行;死亡状态是线程执行完毕或被中断。
线程同步与锁
多线程环境下,共享资源的访问可能导致数据不一致问题。Java提供了synchronized关键字和Lock接口来实现线程同步。synchronized可以修饰方法或代码块,确保同一时间只有一个线程执行该代码。
// synchronized方法
public synchronized void method() {
// 同步代码
}
// synchronized代码块
public void method() {
synchronized(this) {
// 同步代码
}
}
线程间通信
线程间通信可以通过wait、notify和notifyAll方法实现。这些方法必须在同步代码块或同步方法中调用。wait方法使当前线程释放锁并进入等待状态,notify方法唤醒一个等待线程,notifyAll方法唤醒所有等待线程。
// 生产者消费者示例
class Buffer {
private int data;
private boolean available = false;
public synchronized void produce(int value) {
while (available) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
data = value;
available = true;
notifyAll();
}
public synchronized int consume() {
while (!available) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
available = false;
notifyAll();
return data;
}
}
线程池的使用
线程池是一种管理线程的机制,可以减少线程创建和销毁的开销。Java提供了Executor框架来创建和管理线程池。常见的线程池类型包括FixedThreadPool、CachedThreadPool和ScheduledThreadPool。
// 创建固定大小的线程池
ExecutorService executor = Executors.newFixedThreadPool(5);
// 提交任务
executor.submit(() -> {
System.out.println("Task running");
});
// 关闭线程池
executor.shutdown();
死锁问题
死锁是指多个线程互相等待对方释放资源,导致所有线程都无法继续执行。死锁的四个必要条件是互斥条件、占有并等待、不可抢占和循环等待。避免死锁的方法包括破坏其中一个必要条件,例如按顺序获取锁或使用超时机制。
// 死锁示例
class Deadlock {
private final Object lock1 = new Object();
private final Object lock2 = new Object();
public void method1() {
synchronized (lock1) {
synchronized (lock2) {
System.out.println("Method1");
}
}
}
public void method2() {
synchronized (lock2) {
synchronized (lock1) {
System.out.println("Method2");
}
}
}
}
并发工具类
Java提供了多种并发工具类,如CountDownLatch、CyclicBarrier和Semaphore。CountDownLatch用于等待多个线程完成,CyclicBarrier用于多个线程相互等待,Semaphore用于控制资源的访问数量。
// CountDownLatch示例
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
new Thread(() -> {
System.out.println("Thread finished");
latch.countDown();
}).start();
}
latch.await();
System.out.println("All threads finished");
线程安全集合
Java提供了多种线程安全的集合类,如ConcurrentHashMap、CopyOnWriteArrayList和BlockingQueue。这些集合类通过内部同步机制保证多线程环境下的安全性。
// ConcurrentHashMap示例
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("key", 1);
System.out.println(map.get("key"));
volatile关键字
volatile关键字用于确保变量的可见性和禁止指令重排序。volatile变量每次读取都直接从主内存中获取,每次写入都立即刷新到主内存。volatile适用于状态标志等简单场景,但不能保证复合操作的原子性。
// volatile示例
class VolatileExample {
private volatile boolean flag = false;
public void toggle() {
flag = !flag;
}
public boolean isFlag() {
return flag;
}
}
原子类
Java提供了多种原子类,如AtomicInteger、AtomicLong和AtomicReference。这些类通过CAS(Compare-And-Swap)操作保证原子性,适用于计数器等场景。
// AtomicInteger示例
AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet();
System.out.println(counter.get());
线程局部变量
ThreadLocal类用于创建线程局部变量,每个线程都有自己独立的变量副本。ThreadLocal适用于需要避免共享的变量,如数据库连接或用户会话信息。
// ThreadLocal示例
ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);
threadLocal.set(1);
System.out.println(threadLocal.get());
异步编程
Java提供了Future和CompletableFuture支持异步编程。Future表示异步计算的结果,CompletableFuture提供了更丰富的异步操作,如组合多个异步任务。
// CompletableFuture示例
CompletableFuture.supplyAsync(() -> "Hello")
.thenApply(s -> s + " World")
.thenAccept(System.out::println);
线程中断
线程中断是一种协作机制,通过调用interrupt方法设置中断标志,被中断的线程需要检查中断标志并决定是否终止。isInterrupted方法检查中断标志,interrupted方法检查并清除中断标志。
// 线程中断示例
Thread thread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("Running");
}
});
thread.start();
thread.interrupt();
线程优先级
线程优先级用于提示调度器优先调度高优先级线程,但实际执行顺序取决于操作系统。Java中线程优先级范围是1(MIN_PRIORITY)到10(MAX_PRIORITY),默认是5(NORM_PRIORITY)。
// 设置线程优先级
Thread thread = new Thread(() -> System.out.println("Running"));
thread.setPriority(Thread.MAX_PRIORITY);
thread.start();
守护线程
守护线程是为其他线程提供服务的线程,当所有非守护线程结束时,守护线程会自动终止。通过setDaemon方法设置线程为守护线程。
// 守护线程示例
Thread daemon = new Thread(() -> {
while (true) {
System.out.println("Daemon running");
}
});
daemon.setDaemon(true);
daemon.start();
线程组
线程组用于管理一组线程,可以对组内所有线程进行统一操作,如设置优先级或中断。线程组可以嵌套形成树形结构。
// 线程组示例
ThreadGroup group = new ThreadGroup("MyGroup");
Thread thread = new Thread(group, () -> System.out.println("Group thread"));
thread.start();
线程异常处理
线程的未捕获异常可以通过设置UncaughtExceptionHandler进行处理。默认情况下,未捕获异常会导致线程终止,但不会影响其他线程。
// 异常处理示例
Thread thread = new Thread(() -> {
throw new RuntimeException("Error");
});
thread.setUncaughtExceptionHandler((t, e) -> {
System.out.println("Exception in thread " + t.getName() + ": " + e);
});
thread.start();
线程性能优化
多线程性能优化包括减少锁竞争、使用无锁数据结构、合理设置线程池大小等。避免过度同步,缩小同步范围,使用读写锁(ReentrantReadWriteLock)替代独占锁。
// 读写锁示例
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
lock.readLock().lock();
try {
// 读操作
} finally {
lock.readLock().unlock();
}
lock.writeLock().lock();
try {
// 写操作
} finally {
lock.writeLock().unlock();
}
想获取更多学习资料,请关注 GZH 【咖啡 java 研习班】
更多推荐


所有评论(0)