前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【高薪程序员必看】万字长文拆解Java并发编程!(2 2-1)

【高薪程序员必看】万字长文拆解Java并发编程!(2 2-1)

作者头像
摘星.
发布于 2025-05-20 06:20:29
发布于 2025-05-20 06:20:29
10300
代码可运行
举报
文章被收录于专栏:博客专享博客专享
运行总次数:0
代码可运行

2. Java线程

Java线程的执行顺序是由底层的任务调度器去实现的,不由我们控制

2.1. 线程的创建方式

2.1.1. 直接使用Thread

Thread:将线程的创建和任务的创建合并在一起

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//通过Thread创建任务
public static void main(String[] args) {
    Thread t1 = new Thread("t1"){
        @Override
        public void run() {
            log.info("t1");
        }
    };
    t1.start();
    log.info("main");
}
2.1.2. Runnable配合Thread

Runnable:将任务的创建和线程的创建分开,脱离了Thread继承体系,更容易与线程池等高级API结合

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//先创建Runnable任务再创建线程
public static void main(String[] args) {
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            log.info("t1");
        }
    };
    Thread t1 = new Thread(runnable,"t1");
    t1.start();
    log.info("main");
}
2.1.3. FutureTask配合Thread
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static void main(String[] args) throws ExecutionException, InterruptedException {
    FutureTask<String> task = new FutureTask<>(()->{
        log.debug("task");
        return "task";
    });
    Thread t1 = new Thread(task,"t1");
    t1.start();
    log.debug("main");
    log.debug("结果是{}",task.get());
}
2.1.4. lambda表达式

JDK8后会将只有一个抽象方法的接口(例如Runnable)加上@FunctionalInterface表示当前接口可以使用lambda表达式

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//lambda表达式简化写法
public static void main(String[] args) {
    Thread t1 = new Thread(()-> log.info("t1"),"t1");
    t1.start();
    log.info("main");
}

2.2. 查看进程线程

2.2.1. window
  • tasklist:查看进程列表
  • tasklist | findstr xxx:查看指定字符串的进程列表
  • task kill /F /PID:强制杀死指定PID的进程
2.2.2. Linux
  • ps -ef:查看进程列表
  • ps -ef | grep java:查看java进程命令
  • kill -f pid:杀死指定pid的进程
  • top:查看详细进程信息
2.2.3. java
  • jps:查看所有java进程
  • jstack pid:查看这个时刻指定pid的java进程所有线程状态
  • jconsole:图形化界面连接Java进程中的线程运行情况

2.3. 线程运行原理

2.3.1. 栈与栈帧

JavaVirtualMachineStacks(Java虚拟机栈),JVM中由堆、栈、方法区所组成,其中栈内存是给线程用的

  • 每个线程启动后,虚拟机就会为其分配一块栈内存。·
  • 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存·
  • 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法

断点右键指定线程可以调试多个线程运行情况

2.3.2. 线程上下文切换

线程会因为一些原因导致CPU不能继续执行当前线程,转而执行另外一个线程:

  • 当前线程的CPU时间片用完
  • 垃圾回收时,只有执行垃圾回收的线程能运行,其他的工作线程都会被阻塞
  • 有比当前线程更高优先级的线程需要运行
  • 当前线程内部调用了sleep,wait,lock,synchronized,yield,join,park等方法

当线程发生上下文切换(Context Switch)时,操作系统会保留当前线程的运行状态,同时恢复另外一个线程的状态,Java中的完成这个过程的是程序计数器,它会记录下一条字节码指令的地址,线程上下文切换时,由程序计数器提供地址给Java找到继续运行的线程

  • Java中的线程状态包括:程序计数器,栈帧信息,例如局部变量,操作数栈,返回地址等等
  • 频繁发生线程上下文切换会影响程序的性能

2.4. 线程方法对比

2.4.1. start与run

start:

  • 开启线程,并用开启的线程执行run方法.
  • 调用start方法会让线程状态从new状态进入runnable状态
  • start方法只能调用一次,因为线程处于runnable状态时不能再次开启线程

run:

  • 执行run方法,不开启新的线程,调用的只是普通方法.
  • 调用run方法不会对线程状态产生影响
2.4.2. sleep与yield

sleep:

  • 调用sleep方法会让线程状态从running状态进入timewaiting状态
  • 其他线程可以调用interrupt方法打断正在睡眠的线程,此时sleep方法会抛出InterruptedException
  • 睡眠结束的线程可能不会立即被调用
  • TimeUnit的sleep方法比Thread的sleep方法有更好的可读性

yield:

  • 调用yield方法会让线程状态从running状态进入runnable状态,然后调度其他同优先级的runnable状态线程,如果没有同优先级的线程,当前线程还是会运行,相当于是当前线程做出一次让步
  • 具体的实现依赖于操作系统的任务调度器
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-04-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 2. Java线程
    • 2.1. 线程的创建方式
      • 2.1.1. 直接使用Thread
      • 2.1.2. Runnable配合Thread
      • 2.1.3. FutureTask配合Thread
      • 2.1.4. lambda表达式
    • 2.2. 查看进程线程
      • 2.2.1. window
      • 2.2.2. Linux
      • 2.2.3. java
    • 2.3. 线程运行原理
      • 2.3.1. 栈与栈帧
      • 2.3.2. 线程上下文切换
    • 2.4. 线程方法对比
      • 2.4.1. start与run
      • 2.4.2. sleep与yield
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档