Java线程与进程:基础概念解析

一,理解线程和进程

1.进程

后台运行的的软件,程序

2. 什么是线程?

共享资源:同一进程下的线程共享代码段、全局变量和堆内存,这使得线程间通信非常高效。

独立实体:尽管共享资源,但每个线程拥有独立的程序计数器 (PC)、寄存器集合和栈 (Stack),确保其执行流的独立性。

调度单位:操作系统通过调度算法(如时间片轮转)让线程在 CPU 上轮流运行,营造"同时进行"的假象(并发)。

线程是进程的最小执行单元,一个进程可以包含多个线程(单线程进程只有 1 个线程,多线程进程有多个)。

二,创建线程

1,继承 Thread

python 复制代码
class MyThread extends Thread {
    public void run() {
        System.out.println("方式1:继承 Thread 运行");
    }
}
// 启动: new MyThread().start();

缺点:这是最简单的写法,但因为 Java 是单继承机制,继承了 Thread 就无法继承其他类,灵活性差

2,和实现 Runnable接口

java 复制代码
class MyTask implements Runnable {
    public void run() {
        System.out.println("方式2:实现 Runnable 运行");
    }
}
// 启动: new Thread(new MyTask()).start();

缺点:Runnable 的痛点是不能返回值,也不能抛出受检异常。

3,实现 Callable 接口

python 复制代码
class Caller implements Callable<String> {
    public String call() throws Exception {
        return "方式3:带返回值的线程执行结果";
    }
}
// 启动需配合 FutureTask
FutureTask<String> task = new FutureTask<>(new Caller());
new Thread(task).start();
// String result = task.get(); // 阻塞等待获取结果

缺点:Runnable 的痛点是不能返回值,也不能抛出受检异常。Callable 配合 FutureTask 解决了这个问题。

4,线程池

python 复制代码
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class ThreadPoolDemo {
    public static void main(String[] args) {
        // 1. 自定义 ThreadFactory (为了给线程命名)
        ThreadFactory namedThreadFactory = new ThreadFactory() {
            private final AtomicInteger threadNumber = new AtomicInteger(1);
            @Override
            public Thread newThread(Runnable r) {
                Thread t = new Thread(r, "My-Biz-Thread-" + threadNumber.getAndIncrement());
                System.out.println(">>> 创建了一个新线程: " + t.getName());
                return t;
            }
        };

        // 2. 创建线程池
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
            2,                  // corePoolSize: 核心线程数
            4,                  // maximumPoolSize: 最大线程数
            60L, TimeUnit.SECONDS, // keepAliveTime: 空闲存活时间
            new ArrayBlockingQueue<>(2), // workQueue: 有界队列 (容量2)
            namedThreadFactory, // threadFactory: 线程工厂
            new ThreadPoolExecutor.AbortPolicy() // handler: 拒绝策略
        );

        // 3. 模拟提交任务,观察创建过程
        // 任务 1, 2 -> 创建核心线程 (当前线程数: 0->2)
        // 任务 3, 4 -> 进入队列 (队列容量: 0->2)
        // 任务 5, 6 -> 队列满,创建非核心线程 (当前线程数: 2->4)
        // 任务 7    -> 爆满,触发拒绝策略
        for (int i = 1; i <= 7; i++) {
            final int taskId = i;
            try {
                pool.execute(() -> {
                    try { Thread.sleep(2000); } catch (InterruptedException e) {}
                    System.out.println("线程 [" + Thread.currentThread().getName() + "] 完成任务 " + taskId);
                });
            } catch (RejectedExecutionException e) {
                System.err.println("任务 " + taskId + " 被拒绝了");
            }
        }

        pool.shutdown();
    }
}

三,线程池

1,线程池的创建

java 复制代码
public ThreadPoolExecutor(
    int corePoolSize,              // 1. 核心线程数
    int maximumPoolSize,           // 2. 最大线程数
    long keepAliveTime,            // 3. 空闲存活时间
    TimeUnit unit,                 // 4. 时间单位
    BlockingQueue<Runnable> workQueue, // 5. 任务队列
    ThreadFactory threadFactory,   // 6. 线程工厂
    RejectedExecutionHandler handler   // 7. 拒绝策略
)

2,线程池的参数

  • corePoolSize (核心柜员): 银行常驻的柜员窗口。即使没有客户,他们也在那坐着。

  • workQueue (候客区): 核心柜员忙不过来时,客户排队的区域(阻塞队列)。

  • maximumPoolSize (最大柜员): 银行由于客流太大,临时加开的窗口 + 核心窗口的总数。

  • keepAliveTime (临时工下班时间): 临时加开的窗口空闲了多久后会被关闭。

  • threadFactory (工牌制作): 给线程起名字、设置优先级的地方。

  • handler (保安): 窗口满了、排队区也满了,保安出来告诉新来的客户:"今天不办了,请回吧"。

  • threadFactory:线程工程

java 复制代码
 ThreadFactory namedThreadFactory = new ThreadFactory() {
            private final AtomicInteger threadNumber = new AtomicInteger(1);
            @Override
            public Thread newThread(Runnable r) {
              
                return Thread ;
            }

3,拒绝策略

策略类名 行为描述 适用场景 风险
AbortPolicy (默认) 直接抛异常 (RejectedExecutionException),阻止系统正常运行。 默认策略,适合需要感知任务失败的场景。 如果不捕获异常,会导致调用者程序中断。
CallerRunsPolicy "谁调用的谁去跑" 。不抛弃任务,也不抛异常,而是由调用者线程(比如主线程)自己去执行这个任务。 生产环境常用 。它能减缓提交任务的速度(因为主线程去干活了,没空提交新任务),起到负反馈/削峰的作用。 会阻塞主线程,影响系统吞吐量。
DiscardPolicy 直接丢弃任务,不做任何处理,也不抛异常。 适合无关紧要的任务(如某些非核心日志)。 任务丢失且无感知。
DiscardOldestPolicy 丢弃队列里等待最久的一个任务,然后把新任务加进去。 适合且希望能处理最新数据的场景。 会丢失旧数据。

四,线程的方法

1.,启动与执行 (生命周期核心)

方法名 类型 描述 关键点
start() 实例 启动线程 。JVM 会调用底层的操作系统去申请资源,然后自动执行 run() 只能调一次。再次调用会抛异常。
run() 实例 业务逻辑。线程启动后真正执行的代码块。 不要直接调用 !直接调 run() 只是普通方法调用,不会启动新线程。

2.,流程控制 (暂停、等待、让步)

方法名 类型 描述 场景比喻
sleep(long millis) 静态 休眠 。让当前正在执行的线程暂停一段时间,不释放锁 "稍微眯一会儿,但我占着坑不走。"
join() 实例 插队/等待 。调用 t.join() 的线程(通常是主线程)会卡住 ,直到线程 t 执行完毕。 "你先忙,忙完我再继续。" (常用于等待子线程结果)
yield() 静态 让步 。提示 CPU "我可以暂停一下,把机会让给其他同优先级的线程"。但 CPU 不一定理你 "我虽然不累,但可以发扬风格让给别人。"

3, 线程协作 (Object 类的方法)

方法名 描述 关键区别
wait() 等待 。让当前线程暂停,并释放锁(这点最重要),直到被唤醒。 必须在同步代码块中使用。
notify() 唤醒。随机唤醒一个在对象上等待的线程。 就像班主任叫醒一个睡觉的学生。
notifyAll() 全唤醒。唤醒所有在该对象上等待的线程。 就像下课铃响了,所有人都醒了。

Thread.sleep()``Object.wait()``Thread.join()区别

特性 Thread.sleep() Object.wait() Thread.join()
所属类 Thread (静态方法) Object (成员方法) Thread (成员方法)
锁的控制 (最重要!) 死抱锁 🔒 (不释放锁) 撒手锁 🔓 (释放锁,给别人机会) 释放对象锁 (基于 wait 实现,释放被 join 线程对象的锁)
使用位置 任何代码块 必须在 synchronized 块中 任何代码块
唤醒条件 时间到了 别人调 notify() / notifyAll() 被 join 的线程执行完毕 (死掉)
主要用途 暂停/延迟/模拟耗时 线程间通信 (生产者消费者) 控制执行顺序 (T1->T2->T3)

五,线程同步

关键字:synchronized

**1,**同步方法(给整个方法加锁)

这种方式直接在方法声明前添加synchronized关键字,锁的范围是整个方法体。当一个线程进入该方法时,它会自动获取对象实例的锁(对于实例方法)或类的锁(对于静态方法),其他线程必须等待锁释放才能进入。

java 复制代码
public class Counter {
    private int count = 0;

    // 同步方法:整个方法加锁
    public synchronized void increment() {
        count++; // 临界区代码
    }

    public int getCount() {
        return count;
    }
}
 

**2.**同步代码块(给关键代码加锁,更灵活)

这种方式使用synchronized块,指定一个对象作为锁,只锁定需要同步的代码段,而不是整个方法。这提供了更高的灵活性,因为锁的范围更精确

java 复制代码
public class Counter {
    private int count = 0;
    private final Object lock = new Object(); // 锁对象

    public void increment() {
        // 同步代码块:只锁定关键部分
        synchronized(lock) {
            count++; // 临界区代码
        }
    }

    public int getCount() {
        return count;
    }
}
 

**3.**ReentrantLock(可重入锁,更灵活的显式锁)

synchronized更灵活。它支持可重入性(同一线程可以多次获取同一锁),并允许更细粒度的控制,如尝试获取锁、超时机制、公平锁策略等。

java 复制代码
import java.util.concurrent.locks.ReentrantLock;

public class Counter {
    private int count = 0;
    private final ReentrantLock lock = new ReentrantLock(); // 创建ReentrantLock实例

    public void increment() {
        lock.lock(); // 获取锁
        try {
            count++; // 临界区代码
        } finally {
            lock.unlock(); // 确保在finally块中释放锁
        }
    }

    public int getCount() {
        return count;
    }
}
 

在这个例子中,lock.lock()lock.unlock()显式管理锁,确保线程安全。如果使用tryLock(),还可以处理无法获取锁的情况。

synchronized和ReentrantLock比较
特性 synchronized ReentrantLock
实现层面 JVM 关键字 (自动挡) JDK 类 (手动挡)
释放锁 自动释放 (代码执行完或异常时) 必须在 finally 中手动 unlock()
灵活性 只能死等,不可中断 可尝试 (tryLock)、可中断、可超时
公平性 非公平 默认非公平,可设为公平
推荐场景 简单的同步,代码量少 复杂的并发控制,需要高级功能
相关推荐
进击的小头4 小时前
行为型模式:策略模式的C语言实战指南
c语言·开发语言·策略模式
天马37984 小时前
Canvas 倾斜矩形绘制波浪效果
开发语言·前端·javascript
六义义5 小时前
java基础十二
java·数据结构·算法
Tansmjs5 小时前
C++与GPU计算(CUDA)
开发语言·c++·算法
qx095 小时前
esm模块与commonjs模块相互调用的方法
开发语言·前端·javascript
Suchadar5 小时前
if判断语句——Python
开发语言·python
ʚB҉L҉A҉C҉K҉.҉基҉德҉^҉大5 小时前
自动化机器学习(AutoML)库TPOT使用指南
jvm·数据库·python
毕设源码-钟学长6 小时前
【开题答辩全过程】以 基于SpringBoot的智能书城推荐系统的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端
笨手笨脚の6 小时前
深入理解 Java 虚拟机-03 垃圾收集
java·jvm·垃圾回收·标记清除·标记复制·标记整理
莫问前路漫漫6 小时前
WinMerge v2.16.41 中文绿色版深度解析:文件对比与合并的全能工具
java·开发语言·python·jdk·ai编程