JUC 概述
2026/6/10 9:29:59 网站建设 项目流程

并发,并行,串行

  • 并发:宏观上同时进行,微观上交替切换执行,单个 CPU 核心,靠快速切换任务
  • 并行:同一时刻,多个任务真正同时运行,多个 CPU 核心,每个核心独立跑一个任务
  • 串行:同一时刻只做一件事,做完一件再做下一件

线程

什么是线程

一个程序运行时,内部可以分成一个或多个线程,线程是CPU 执行任务的最小单位

创建线程的方式

实现 Runnable

classMyRunnableimplementsRunnable{@Overridepublicvoidrun(){System.out.println("Runnable 线程:"+Thread.currentThread().getName());}}publicclassTest{publicstaticvoidmain(String[]args){MyRunnabler=newMyRunnable();Threadt=newThread(r);t.start();}}

继承 Thread

classMyThreadextendsThread{@Overridepublicvoidrun(){System.out.println("线程运行:"+Thread.currentThread().getName());}}publicclassTest{publicstaticvoidmain(String[]args){MyThreadt=newMyThread();t.start();// 启动线程}}

实现 Callable

classMyCallableimplementsCallable<Integer>{@OverridepublicIntegercall()throwsException{System.out.println("Callable 线程运行");return100;// 可以返回结果}}publicclassTest{publicstaticvoidmain(String[]args)throwsException{FutureTask<Integer>task=newFutureTask<>(newMyCallable());Threadt=newThread(task);t.start();// 获取返回值Integerres=task.get();System.out.println("结果:"+res);}}

线程池创建

publicclassTest{publicstaticvoidmain(String[]args){// 创建线程池ExecutorServicepool=Executors.newFixedThreadPool(3);// 提交任务pool.submit(()->{System.out.println("线程池线程:"+Thread.currentThread().getName());});pool.shutdown();// 关闭线程池}}

线程的生命周期

  1. 新建,创建了一个线程对象,但是没有调用 start() 方法
  2. 就绪,调用了 start(),等着 CPU 分配时间片,一旦拿到 CPU,就开始执行 run()
  3. 阻塞,线程阻塞于锁
  4. 等待,主动无限期等别人唤醒,例如:wait() / join() / LockSupport.park()
  5. 定时等待,有时间限制的等待,例如:sleep(1000) / wait(1000) / join(1000)
  6. 终止,run() 执行完 / 异常退出

状态的流转关系

NEW → start() → RUNNABLE RUNNABLE ↓ 抢锁失败 BLOCKED → 拿到锁 → RUNNABLE RUNNABLE ↓ wait()/join() WAITING → notify()/notifyAll() → RUNNABLE RUNNABLE ↓ sleep(1000) TIMED_WAITING → 时间到 → RUNNABLE RUNNABLE → 执行完毕 → TERMINATED

如何终止线程

stop

强制杀死线程,已弃用

interrupt
publicclassTest{publicstaticvoidmain(String[]args)throwsInterruptedException{Threadt=newThread(()->{try{while(!Thread.currentThread().isInterrupted()){System.out.println("运行中...");Thread.sleep(1000);}}catch(InterruptedExceptione){// 收到中断信号,退出System.out.println("线程被中断终止");}});t.start();Thread.sleep(2000);t.interrupt();// 中断}}
interrupt()、interrupted()、isInterrupted()的作用以及区别
  1. thread.interrupt()
    作用:中断线程(设置 interupted = true)
    类型:实例方法
    效果:
    如果线程正在运行:仅设置中断标志,不停止运行
    如果线程在 sleep()/wait()/join():会抛出 InterruptedException,并清除中断标志
    不会强制停止线程,只是通知

  2. thread.isInterrupted()
    作用:查询是否被中断
    类型:实例方法
    特点:
    只读取,不改变状态
    调用一次,标记依然保留

  3. Thread.interrupted()
    作用:查询 + 清除中断标记
    类型:静态方法
    特点:
    第一次调用返回 true
    第二次调用一定返回 false(因为复位了)
    只针对当前执行的线程,例如在 main 线程中执行 t1.interrupted(),其实查询的是 main 线程的 interupted 变量并复位

synchronized

锁升级

无锁 → 偏向锁 → 轻量级锁 → 重量级锁

单向升级,不可降级,只有释放锁后才会变回无锁

无锁

默认的无锁状态

偏向锁(jdk 15+ 默认取消偏向锁)

锁一直被同一个线程拿

第一次抢锁:CAS 把当前线程 ID 写入对象头,之后再来,只要线程 ID 匹配,直接进,不需要 CAS

第二个线程来竞争时,偏向锁被撤销 → 升级轻量级锁

轻量级锁

两个线程交替用锁,无长时间阻塞

参考

线程在自己的栈帧创建 Lock Record,把对象头复制到 Lock Record 中,通过 CAS 把对象头换成指向自己 Lock Record 的指针

成功:拿到锁

失败:将锁升级为重量级锁,然后进入 cxq 链表进行自旋,自旋成功则获取锁,自旋多次失败则会进入阻塞状态,等待唤醒

重量级锁

monitor-enter

每一个对象都会和一个监视器monitor关联, 监视器被占用时会被锁住, 其他线程无法来获取该监视器, 当JVM执行某个对象的 monitorenter(synchronized(obj)) 方法时, 会尝试去获取对象的监视器所有权, 过程如下:

  1. 如果monitor 的进入数为 0, 则线程可以进入monitor, 并将 monitor 的进入数变为 1, 当前线程成为 monitor 的所有者
  2. 如果线程已经拥有了 monitor 的所有权, 允许重入 monitor, 此时 monitor 的进入数 +1
  3. 如果其他线程已经占有了 monitor, 当前线程尝试获取 monitor 所有权时会被阻塞, 直到 monitor 的进入数为 0
monitor-exit
  1. 能执行monitorexit指令的线程一定是拥有monitor所有权的线程
  2. 执行monitorexit指令后, monitor 的进入数 -1, 当进入数变为 0 后, 表示当前线程释放锁, 不再拥有 monitor 的所有权, 此时其他阻塞的线程可以去尝试获取monitor的所有权

synchronized 执行时, 没有竞争到锁的线程会被挂起, 此时需要调用操作系统的park() 方法, 竞争到锁的线程会被 unpark() 唤醒

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询