当前位置: 首页 > news >正文

Java多线程超详学习内容

一、多线程基础概念

线程与进程:

 进程是程序的一次执行过程,是系统资源分配的基本单位(如一个运行中的浏览器)。

线程是进程内的执行单元,是CPU调度的基本单位,一个进程可包含多个线程(如浏览器中同时加载页面、播放视频的线程)。

线程共享进程的内存资源(如堆、方法区),但有自己独立的栈和程序计数器。

多线程的意义:

提高程序执行效率(如并发处理多个任务,避免单线程阻塞导致的资源浪费)。

提升用户体验(如UI线程与后台数据加载线程分离,避免界面卡顿)。

线程的状态:

新建(New):线程对象创建后未启动的状态。

就绪(Runnable):调用 start() 后,线程等待CPU调度的状态(包含运行中状态)。

阻塞(Blocked):因竞争同步锁被阻塞的状态(如未获取 synchronized 锁)。

等待(Waiting):无时间限制的等待(如调用 wait() 、 join() 无参方法)。

超时等待(Timed Waiting):有时间限制的等待(如 sleep(1000) 、 wait(1000) )。

终止(Terminated):线程执行完毕或异常终止的状态。

二、线程的创建与启动

1. 继承 Thread 类

步骤:

1. 自定义类继承 Thread ,重写 run() 方法(线程执行的任务)。

2. 创建线程对象,调用 start() 方法启动线程(而非直接调用 run() ,否则为普通方法调用)。

示例:

class MyThread extends Thread {

    @Override

    public void run() {

        System.out.println("线程执行任务");

    }

}

public class Test {

    public static void main(String[] args) {

        MyThread t = new MyThread();

        t.start(); // 启动线程

    }

}

2. 实现 Runnable 接口

步骤:

1. 自定义类实现 Runnable 接口,重写 run() 方法。

2. 创建 Runnable 实例,作为参数传入 Thread 构造器,调用 start() 启动。

优势:避免单继承限制,可共享资源(多个线程共享一个 Runnable 实例)。

示例:

class MyRunnable implements Runnable {

    @Override

    public void run() {

        System.out.println("线程执行任务");

    }

}

public class Test {

    public static void main(String[] args) {

        Thread t = new Thread(new MyRunnable());

        t.start();

    }

}

3. 实现 Callable 与 Future 

特点:可返回结果、可抛出受检异常(相比 Runnable 更灵活)。

步骤:

1. 实现 Callable<T> 接口,重写 call() 方法(返回值类型为 T )。

2. 通过 FutureTask<T> 包装 Callable 实例,作为参数传入 Thread 。

3. 调用 FutureTask.get() 获取返回结果(会阻塞当前线程直到结果返回)。

示例:

import java.util.concurrent.Callable;

import java.util.concurrent.FutureTask;

class MyCallable implements Callable<Integer> {

    @Override

    public Integer call() throws Exception {

        return 1 + 1;

    }

}

public class Test {

    public static void main(String[] args) throws Exception {

        FutureTask<Integer> task = new FutureTask<>(new MyCallable());

        new Thread(task).start();

        System.out.println("结果:" + task.get()); // 输出2

    }

}

4. 线程池创建(推荐)

通过 ExecutorService 线程池管理线程(减少线程创建/销毁开销,控制并发数)。

常用方法: Executors.newFixedThreadPool(n) (固定大小线程池)、 newCachedThreadPool() (缓存线程池)等。

示例:

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

public class Test {

    public static void main(String[] args) {

        ExecutorService pool = Executors.newFixedThreadPool(3); // 3个线程的线程池

        pool.submit(new MyRunnable()); // 提交任务

        pool.shutdown(); // 关闭线程池

    }

}

三、线程的常用方法

start() :启动线程,使线程进入就绪状态(由CPU调度执行 run() )。

run() :定义线程执行的任务(直接调用仅为普通方法,不会启动新线程)。

sleep(long millis) :让当前线程休眠指定毫秒数(进入超时等待状态,不释放锁)。

wait() / notify() / notifyAll() :

用于线程间通信,需在 synchronized 代码块/方法中调用。

wait() :当前线程释放锁,进入等待状态。

notify() :唤醒一个等待该锁的线程; notifyAll() :唤醒所有等待该锁的线程。

join() :让当前线程等待调用线程执行完毕后再继续(如 t.join() ,主线程等待t执行完)。

yield() :当前线程让出CPU资源,重新进入就绪状态(可能立即被再次调度)。

interrupt() :中断线程(设置中断标志,需线程主动检测标志并处理,如退出循环)。

isAlive() :判断线程是否存活(处于就绪、运行、阻塞、等待状态时为 true )。

四、线程同步(解决线程安全问题)

1. 线程安全问题

原因:多个线程共享资源,且操作非原子性(如多线程同时修改同一变量)。

示例:两个线程同时对 count 加1,可能导致结果小于预期(因 count++ 拆分为“读取-修改-写入”三步,中间可能被其他线程打断)。

2. 同步机制

(1) synchronized 关键字

作用:保证同一时刻只有一个线程执行同步代码,实现互斥。

用法:

修饰方法: public synchronized void method() (锁为当前对象 this )。

修饰静态方法: public static synchronized void method() (锁为类的 Class 对象)。

修饰代码块: synchronized(锁对象) { ... } (锁对象可为任意对象,推荐用 this 或 Class 对象)。

原理:通过“对象监视器”(monitor)实现,线程获取锁后进入同步区域,释放锁后其他线程可竞争。

(2) Lock 接口(JDK 1.5+)

相比 synchronized 的优势:可手动获取/释放锁、可中断获取锁、可超时获取锁、支持公平锁。

常用实现类: ReentrantLock (可重入锁,与 synchronized 一样支持重入)。

用法:

import java.util.concurrent.locks.Lock;

import java.util.concurrent.locks.ReentrantLock;

public class Test {

    private Lock lock = new ReentrantLock();

    public void method() {

        lock.lock(); // 获取锁

        try {

            // 同步代码

        } finally {

            lock.unlock(); // 释放锁(必须在finally中,避免异常导致锁未释放)

        }

    }

}

(3)原子类( java.util.concurrent.atomic )

用于解决基本数据类型的线程安全问题,基于CAS(Compare And Swap)机制实现(无锁操作,效率高)。

常用类: AtomicInteger 、 AtomicLong 、 AtomicBoolean 等。

- 示例: AtomicInteger count = new AtomicInteger(0); count.incrementAndGet(); (原子性自增)。

(4)线程安全的集合

Vector 、 Hashtable (古老,效率低,基于 synchronized )。

Collections.synchronizedXXX() :将非线程安全集合转为线程安全(如 synchronizedList(list) )。

java.util.concurrent 包下的集合: ConcurrentHashMap 、 CopyOnWriteArrayList 等(高效,基于分段锁、写时复制等机制)。

五、线程通信

 目的:协调多个线程的执行顺序(如“生产者-消费者”模型中,生产者生产后通知消费者,消费者消费完通知生产者)。

实现方式:

wait() / notify() / notifyAll() (结合 synchronized )。

Condition (结合 Lock ,更灵活,可创建多个条件):

Lock lock = new ReentrantLock();

Condition notEmpty = lock.newCondition(); // 非空条件

Condition notFull = lock.newCondition(); // 非满条件

// 等待:notEmpty.await();

// 唤醒:notEmpty.signal();

管道流( PipedInputStream / PipedOutputStream ):用于线程间字节流通信。

六、线程池详解

1. 核心参数( ThreadPoolExecutor 构造器)

corePoolSize :核心线程数(线程池长期保留的线程数,即使空闲)。

maximumPoolSize :最大线程数(核心线程+临时线程的最大数量)。

keepAliveTime :临时线程的空闲存活时间(超过该时间销毁)。

unit : keepAliveTime 的单位(如 TimeUnit.SECONDS )。

workQueue :任务队列(核心线程满时,新任务放入队列)。

threadFactory :线程创建工厂(自定义线程名称、优先级等)。

handler :拒绝策略(当队列满且线程数达最大值时,处理新任务的方式):

AbortPolicy (默认):抛出 RejectedExecutionException 。

CallerRunsPolicy :让提交任务的线程自己执行。

DiscardPolicy :直接丢弃新任务。

DiscardOldestPolicy :丢弃队列中最旧的任务,再提交新任务。

2. 工作原理

1. 新任务提交时,若核心线程未满,创建核心线程执行任务。

2. 核心线程满时,任务放入工作队列。

3. 队列满时,若未达最大线程数,创建临时线程执行任务。

4. 线程数达最大值且队列满时,触发拒绝策略。

3. 常用线程池

 Executors.newFixedThreadPool(n) :固定大小线程池(核心线程=最大线程,无临时线程)。

Executors.newCachedThreadPool() :缓存线程池(核心线程=0,最大线程=Integer.MAX_VALUE,临时线程空闲60s销毁,适合短期任务)。

Executors.newSingleThreadExecutor() :单线程池(核心线程=最大线程=1,任务按顺序执行)。

Executors.newScheduledThreadPool(n) :定时任务线程池(支持延迟执行、周期性执行)。

七、并发工具类

CountDownLatch :倒计时器(让主线程等待多个子线程执行完毕后再继续)。

countDown() :计数器减1; await() :等待计数器为0。

CyclicBarrier :循环屏障(让多个线程到达屏障点后再一起继续执行,可重复使用)。

await() :线程到达屏障点后等待,所有线程到达后继续。

Semaphore :信号量(控制同时访问资源的线程数,如限流)。

acquire() :获取许可; release() :释放许可。

Exchanger :用于两个线程交换数据( exchange() 方法阻塞等待对方线程交换)。

八、线程的高级特性

线程组( ThreadGroup ):管理一组线程,可批量设置优先级、中断等(实际开发中较少使用,推荐线程池)。

线程优先级:范围1-10(默认5),优先级高的线程更可能被CPU调度(不保证绝对优先,依赖操作系统)。

守护线程(Daemon Thread):为其他线程服务(如GC线程),当所有非守护线程结束,守护线程自动终止。

通过 setDaemon(true) 设置(需在 start() 前调用)。

线程局部变量( ThreadLocal ):为每个线程提供独立的变量副本,避免线程安全问题(如 SimpleDateFormat 非线程安全,用 ThreadLocal 包装后每个线程单独持有一个实例)。

方法: set(T) 、 get() 、 remove() (使用后需 remove() ,避免内存泄漏)。

九、并发编程问题与解决方案

死锁:

原因:多个线程互相持有对方需要的锁,且不释放。

避免:按顺序获取锁、设置锁超时、使用 Lock 的 tryLock() 检测。

活锁:线程不断重试获取资源,导致任务无法推进(如两个线程互相释放锁后立即再次获取,陷入循环)。

解决:引入随机延迟重试。

饥饿:低优先级线程长期得不到CPU调度(如高优先级线程持续占用资源)。

解决:合理设置优先级、使用公平锁( ReentrantLock(true) )。

以上内容从基础到进阶,实际学习中需多写代码实践,理解线程同步、通信的场景和问题。

http://www.dtcms.com/a/355748.html

相关文章:

  • Python 中的反射机制与动态灵活性
  • Spring学习笔记:Spring JDBC(jdbc Template)的深入学习和使用
  • 行业前瞻:在线教育系统源码与网校APP开发的技术进化方向
  • C++学习笔记之异常处理
  • Pruning-Guided Curriculum Learning
  • 机器视觉学习-day06-图像旋转
  • MPPT的基本原理
  • 如何循环同步下载文件
  • Yolov8 pose 推理部署笔记
  • HTML应用指南:利用POST请求获取全国中国工商银行网点位置信息
  • 序列化,应用层自定义协议
  • 万博智云联合华为云共建高度自动化的云容灾基线解决方案
  • 浅谈JMeter Listener
  • 自学嵌入式第三十天:Linux系统编程-线程的控制
  • 因果推断在解决多触点归因问题上的必要性
  • 利用ollama部署本地大模型 离线使用
  • 告别Java依赖!GISBox三维场景编辑+服务发布一站式工具横评
  • 模型汇总-数学建模
  • echarts碰到el-tabs首次加载echarts宽度只有100px
  • LoRA模型的可训练参数解析(61)
  • 杂记 08
  • CnSTD+CnOCR的联合使用
  • vsgCs显示谷歌全球倾斜模型-节点
  • 9 从 “内存怎么存” 到 “指针怎么用”:计算机内存编址机制 + C 语言指针核心 + memory 模拟实现
  • “AI+制造”政策下,户外智能清洁如何跑出加速度?
  • 20250828-学习JumpServer开源堡垒机使用:统一访问入口 + 安全管控 + 操作审计
  • 复杂BI报表SQL
  • 集成电路学习:什么是TensorFlow
  • MCP Factory——专业 MCP Server 开发平台,正式发布,免费下载!
  • 一站式爬虫MCP服务器助力AI市场分析师