java 线程、锁优缺点汇总及样例-个人总结

线程

1. 基本概念

线程的生命周期

  • NEW(新建):线程对象被创建,但尚未调用 start() 方法。
  • RUNNABLE(可运行):调用 start() 方法后,线程进入可运行状态,等待 CPU 调度。
  • BLOCKED(阻塞):线程被阻塞,等待获取锁。
  • WAITING(等待):线程等待另一个线程的通知或信号。
  • TIMED_WAITING(计时等待):线程等待另一个线程的通知或信号,带有超时时间。
  • TERMINATED(终止):线程执行完毕或因异常退出。

2. 创建线程的方式

2.1 继承 Thread 类

优点:简单易用,适合简单任务。缺点:无法继承其他类,灵活性较差。

代码示例:

// 继承 Thread 类创建线程 public class MyThread extends Thread { @Override public void run() { // 线程执行的任务 System.out.println("线程正在运行"); } public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); // 启动线程 } }

真实场景:适用于简单的后台任务,如日志记录、文件监控等场景。

2.2 实现 Runnable 接口

优点:可以实现多继承,灵活性高。缺点:需要额外的线程启动代码。

代码示例:

// 实现 Runnable 接口创建线程 public class MyRunnable implements Runnable { @Override public void run() { // 线程执行的任务 System.out.println("Runnable 正在运行"); } public static void main(String[] args) { Thread thread = new Thread(new MyRunnable()); thread.start(); // 启动线程 } }

真实场景:适用于需要实现复杂任务逻辑的场景,或在已有的类中实现多线程功能。

2.3 使用 Callable 接口

优点:可以返回结果并抛出异常。缺点:需要使用 Future 获取结果,使用稍微复杂。

代码示例:

import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; // 实现 Callable 接口创建线程 public class MyCallable implements Callable<String> { @Override public String call() throws Exception { // 线程执行的任务 return "Callable 结果"; } public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService executor = Executors.newSingleThreadExecutor(); Future<String> future = executor.submit(new MyCallable()); // 获取线程返回的结果 System.out.println(future.get()); executor.shutdown(); // 关闭线程池 } }

真实场景:适用于需要异步计算并获取结果的任务,如数据处理、计算任务等。

3. 线程池

3.1 FixedThreadPool

优点:线程数固定,控制最大并发数。缺点:线程数固定,任务可能会排队等待。

代码示例:

import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class FixedThreadPoolExample { public static void main(String[] args) { // 创建固定大小的线程池 ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10); for (int i = 0; i < 20; i++) { final int taskId = i; fixedThreadPool.execute(() -> { // 执行任务 System.out.println("处理任务 " + taskId + " 在线程 " + Thread.currentThread().getName()); }); } fixedThreadPool.shutdown(); // 关闭线程池 } }

真实场景:适用于需要控制最大并发线程数的场景,如服务器处理请求、后台任务处理等。

3.2 CachedThreadPool

优点:线程数不固定,空闲线程可回收。缺点:可能导致线程数量过多,资源耗尽。

代码示例:

import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class CachedThreadPoolExample { public static void main(String[] args) { // 创建可缓存的线程池 ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); for (int i = 0; i < 20; i++) { final int taskId = i; cachedThreadPool.execute(() -> { // 执行任务 System.out.println("处理任务 " + taskId + " 在线程 " + Thread.currentThread().getName()); }); } cachedThreadPool.shutdown(); // 关闭线程池 } }

真实场景:适用于短时间内需要处理大量任务的场景,如实时数据处理、任务调度等。

3.3 ScheduledThreadPool

优点:支持定时和周期性任务。缺点:线程数固定,任务可能会排队等待。

代码示例:

import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class ScheduledThreadPoolExample { public static void main(String[] args) { // 创建定时任务线程池 ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); // 定时任务,每秒执行一次 scheduledThreadPool.scheduleAtFixedRate(() -> { System.out.println("定时任务执行,在线程 " + Thread.currentThread().getName()); }, 0, 1, TimeUnit.SECONDS); // 运行 5 秒后关闭线程池 Executors.newSingleThreadScheduledExecutor().schedule(() -> { scheduledThreadPool.shutdown(); }, 5, TimeUnit.SECONDS); } }

真实场景:适用于定时任务和周期性任务,如定时备份、周期性数据同步等。

3.4 SingleThreadExecutor

优点:保证任务顺序执行。缺点:效率较低,仅适用于顺序任务。

代码示例:

import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SingleThreadExecutorExample { public static void main(String[] args) { // 创建单线程池 ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); for (int i = 0; i < 5; i++) { final int taskId = i; singleThreadExecutor.execute(() -> { // 执行任务 System.out.println("处理任务 " + taskId + " 在线程 " + Thread.currentThread().getName()); }); } singleThreadExecutor.shutdown(); // 关闭线程池 } }

真实场景:适用于需要按顺序执行任务的场景,如日志处理、任务队列等。

3.5 ThreadPoolTaskExecutor

    • ThreadPoolTaskExecutor 是 Spring 提供的一个方便的包装类,基于 Java 的 ThreadPoolExecutor,用于执行异步任务。
  1. availableProcessors
    • Runtime.getRuntime().availableProcessors() 返回当前 JVM 可用的处理器核心数量。它用于动态设置线程池的大小,以适应不同的硬件环境。
  1. maxPoolSize
    • maxPoolSize 是线程池中允许的最大线程数。此代码中将其设置为可用处理器数量的一半,但最少为1。目的是避免创建过多的线程,从而提高系统的效率。
  1. corePoolSize
    • corePoolSize 是线程池中保持活跃的核心线程数。此代码中将其设置为最大线程数的一半,但最少为1。核心线程在空闲时不会被回收,确保线程池有足够的线程处理任务。
  1. Queue Capacity
    • setQueueCapacity(queueCapacity) 设置线程池的队列容量。队列用于存储等待执行的任务,当所有核心线程都在忙碌时,新任务会进入队列等待。
  1. Thread Name Prefix
    • setThreadNamePrefix(threadName) 设置线程池中线程的名称前缀,有助于调试和监控。
  1. RejectedExecutionHandler
    • setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()) 设置任务拒绝策略。当线程池达到最大线程数并且队列已满时,决定如何处理新任务。
  1. Keep Alive Seconds
    • setKeepAliveSeconds(keepAliveSeconds) 设置线程空闲时间,当线程空闲时间超过这个值时,非核心线程会被回收。

setRejectedExecutionHandler 策略说明

RejectedExecutionHandler 是 java.util.concurrent.ThreadPoolExecutor 的一个内部接口,用于处理当线程池已达最大线程数且队列已满时,无法执行新任务的情况。常见的拒绝策略包括:

  1. AbortPolicy

(默认策略):

    • 抛出 RejectedExecutionException,通知调用者任务被拒绝。
    • 示例:

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());

  1. CallerRunsPolicy
    • 直接在调用者线程中运行该任务。如果线程池已经关闭,则丢弃该任务。
    • 优点:不会丢失任务,但可能会影响调用者线程的性能。
    • 示例:

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

  1. DiscardPolicy
    • 丢弃无法处理的任务,不抛出异常。
    • 示例:

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());

  1. DiscardOldestPolicy
    • 丢弃队列中最旧的未处理任务,然后重新尝试执行当前任务。
    • 示例:

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());

public ThreadPoolTaskExecutor createExecutor(int queueCapacity, String threadName, int keepAliveSeconds) { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 系统核心处理器数量 int availableProcessors = Runtime.getRuntime().availableProcessors(); // 最大线程数 是 系统核心处理器的 50% int maxPoolSize = Math.max(availableProcessors / 2, 1); // 核心线程数 是 最大线程数的 50% int corePoolSize = Math.max(maxPoolSize / 2, 1); // 配置核心线程数 executor.setCorePoolSize(corePoolSize); // 配置最大线程数 executor.setMaxPoolSize(maxPoolSize); // 配置队列大小 executor.setQueueCapacity(queueCapacity); // 配置线程池中的线程的名称前缀 executor.setThreadNamePrefix(threadName); // 配置线程池中的线程的存活时间 executor.setKeepAliveSeconds(keepAliveSeconds); // rejection-policy:当pool已经达到max size的时候,如何处理新任务 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 执行初始化 executor.initialize(); return executor; }

4.锁

1. synchronized 锁

优点:简单易用,隐式锁定和解锁。缺点:可能导致性能瓶颈,阻塞线程。

代码示例:

public class SynchronizedExample { private int count = 0; // 使用同步方法保证线程安全 public synchronized void increment() { count++; } public void showCount() { System.out.println("当前计数: " + count); } public static void main(String[] args) { SynchronizedExample example = new SynchronizedExample(); Runnable task = () -> { for (int i = 0; i < 1000; i++) { example.increment(); } }; Thread t1 = new Thread(task); Thread t2 = new Thread(task); t1.start(); t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } example.showCount(); // 输出结果应为2000 } }

真实场景:适用于需要简单同步的场景,如单例模式、数据一致性保障等。

2. ReentrantLock(重入锁)

优点:可重入,灵活性高,支持条件变量。缺点:需要显式锁定和解锁,代码复杂性增加。

代码示例:

import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ReentrantLockExample { private final Lock lock = new ReentrantLock(); private int count = 0; public void increment() { lock.lock(); // 获取锁 try { count++; } finally { lock.unlock(); // 释放锁 } } public void showCount() { System.out.println("当前计数: " + count); } public static void main(String[] args) { ReentrantLockExample example = new ReentrantLockExample(); Runnable task = () -> { for (int i = 0; i < 1000; i++) { example.increment(); } }; Thread t1 = new Thread(task); Thread t2 = new Thread(task); t1.start(); t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } example.showCount(); // 输出结果应为2000 } }

真实场景:适用于需要复杂锁操作的场景,如数据库操作、线程安全的数据结构等。

3. ReadWriteLock(读写锁)

优点:允许多个线程并发读取,写操作是排他性的。缺点:读写锁逻辑复杂,读操作较多时可能导致写操作等待时间增加。

代码示例:

import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ReadWriteLockExample { private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); private String data = "初始数据"; public String readData() { readWriteLock.readLock().lock(); // 获取读锁 try { return data; } finally { readWriteLock.readLock().unlock(); // 释放读锁 } } public void writeData(String newData) { readWriteLock.writeLock().lock(); // 获取写锁 try { data = newData; } finally { readWriteLock.writeLock().unlock(); // 释放写锁 } } public static void main(String[] args) { ReadWriteLockExample example = new ReadWriteLockExample(); Runnable readTask = () -> System.out.println("读取数据: " + example.readData()); Runnable writeTask = () -> example.writeData("新数据"); // 启动多个线程进行读写操作 for (int i = 0; i < 5; i++) { new Thread(readTask).start(); } for (int i = 0; i < 2; i++) { new Thread(writeTask).start(); } } }

真实场景:适用于读操作远多于写操作的场景,如缓存系统、配置读取等。

4. StampedLock(印章锁)

优点:比读写锁效率更高,支持无锁读操作。缺点:复杂性高,使用不当可能引入错误。

代码示例:

import java.util.concurrent.locks.StampedLock; public class StampedLockExample { private final StampedLock stampedLock = new StampedLock(); private String data = "初始数据"; public String readData() { long stamp = stampedLock.readLock(); // 获取读锁 try { return data; } finally { stampedLock.unlockRead(stamp); // 释放读锁 } } public void writeData(String newData) { long stamp = stampedLock.writeLock(); // 获取写锁 try { data = newData; } finally { stampedLock.unlockWrite(stamp); // 释放写锁 } } public static void main(String[] args) { StampedLockExample example = new StampedLockExample(); Runnable readTask = () -> System.out.println("读取数据: " + example.readData()); Runnable writeTask = () -> example.writeData("新数据"); // 启动多个线程进行读写操作 for (int i = 0; i < 5; i++) { new Thread(readTask).start(); } for (int i = 0; i < 2; i++) { new Thread(writeTask).start(); } } }

真实场景:适用于读多写少的场景,对性能要求高的应用,如高频率数据查询和修改的系统。

  • 10
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值