JavaDriver JavaDriver
首页
  • 基础
  • 并发
  • JVM
  • 设计模式
  • 计算机网络
  • 操作系统
  • 数据结构
  • 算法
  • MYSQL
  • REDIS
  • Netty
  • Kafka
系统设计
非技术
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

YoungAnn

西二旗Java老司机一枚 致力于社会主义添砖Java
首页
  • 基础
  • 并发
  • JVM
  • 设计模式
  • 计算机网络
  • 操作系统
  • 数据结构
  • 算法
  • MYSQL
  • REDIS
  • Netty
  • Kafka
系统设计
非技术
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 基础

  • 并发

    • 线程池是如何实现的?
    • 简述 CAS 原理,什么是 ABA 问题,怎么解决?
    • 简述 Synchronized,Volatile,可重入锁的不同使用场景及优缺点
    • Synchronized 与 Lock 相比优缺点分别是什么?
    • 重入锁是如何实现的?
    • volatile 关键字解决了什么问题,它的实现原理是什么?
    • 简述 Java 锁升级的机制
    • 简述 Java AQS 的原理以及使用场景
    • 什么是公平锁?什么是非公平锁?
    • Java 的线程有哪些状态,转换关系是怎么样的?
    • Java 是如何实现线程安全的,哪些数据结构是线程安全的?
    • 手写死锁
    • 为什么我们不能直接调用 run() 方法?
    • Java 线程有哪些常用方法?
      • Java 线程如何启动?如何终止?
      • 构造线程
      • 启动线程
      • 理解中断
      • 已不建议使用的方法:suspend(),resume(),stop()
      • 安全的终止线程
    • 手写生产者消费者模型
    • ThreadLocal 实现原理是什么?为什么要使用弱引用?
  • JVM

  • 设计模式

  • Java相关
  • 并发
YoungAnn
2022-04-04
目录

Java 线程有哪些常用方法?

# Java 线程如何启动?如何终止?

线程随着start()方法的调用而启动,随着run()方法调用结束而终止。 但是这其中还会涉及到线程的构造、中断、暂停、恢复,我们来一一看下。

# 构造线程

在运行一个线程之前 肯定要先构造一个线程对象,构造线程对象需要提供所需属性:如线程所属组,线程优先级,是否deamon等。

private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc) {
    //1.设置线程名
    if (name == null) {
        throw new NullPointerException("name cannot be null");
    }

    this.name = name;
    //2.设置线程的线程组,如果未指定,则此线程的线程组为当前初始化线程的线程组
    Thread parent = currentThread();
    ...
    if (g == null) {
        //这里有一段注释说,安卓不支持SecurityManager,所以删除了相关代码
        g = parent.getThreadGroup();
    }

    //所属线程组未启动线程计数+1
    g.addUnstarted();

    this.group = g;
    //3.设置线程的其他属性
    //线程是否是守护线程以及优先级都继承自当前线程
    this.daemon = parent.isDaemon();
    this.priority = parent.getPriority();
    this.target = target;
    //4.调用init2 执行剩余初始化操作
    init2(parent);
    //5.设置线程的堆栈大小。但是否生效要看虚拟机。那么在art虚拟机上是否生效呢,我们后续再说
    /* Stash the specified stack size in case the VM cares */
    this.stackSize = stackSize;

    //6.设置当前线程id
    tid = nextThreadID();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

到这里为止,我们的thread就已经初始化完毕了。在堆内存中等待这被执行start()方法开始运行。

# 启动线程

调用start()方法开始启动线程。

# 理解中断

如果线程需要执行一个长时间任务,就可能需要能中断线程。中断线程就是其他线程给该线程发一个信号,该线程收到信号后结束执行run()方法,使得自身线程能立刻结束运行。

中断可以理解为线程的一个标志位属性,调用interrupt()方法中断线程仅仅是把中断标志位置为true,线程可以通过检查自身的标志位来判断是否被中断了。

另外还有两点需要注意的:

  • 如果线程处于终结状态,即使线程被中断过,其中断标识位依然是false
  • 许多声明抛出InterruptException的方法(如Thread.sleep(long millis)),在抛出InterruptException之前会将中断标识位置为false。

# 已不建议使用的方法:suspend(),resume(),stop()

suspend(),resume(),stop()完成了线程的暂停、恢复、停止。但是这些方法过期了 不建议继续使用,原因是:

  1. suspend()暂停线程是不会释放资源(比如锁),而是占着资源入睡 ,这样容易引发死锁
  2. stop() 终止线程时没有给线程清理资源的机会,导致程序运行在不确定状态

取而代之的是wait()和notify()/notifyAll()。

# 安全的终止线程

可以通过interrupt()或者标识位来优雅的停止线程。

public class MyRunner implements Runnable {
  private volatile boolean on = true;
    public void run() {
        while(on){
          System.out.println("Running...");
        }
        System.out.println("Stoped!");
    }

    public void cancel(){
      on  = false;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

通过标识位来停止线程

public class ShutdownThread {

    public static void main(String[] args) {
        Thread t1 = new Thread(new MyRunner().start();
          t1.cancel();
    }
}
1
2
3
4
5
6
7

通过interrupt()来停止线程

public class ShutdownThread {

    public static void main(String[] args) {
        Thread t1 = new Thread(new MyRunner().start();
          t1.interrupt();
    }
}
1
2
3
4
5
6
编辑 (opens new window)
上次更新: 2022/05/19, 21:26:01
为什么我们不能直接调用 run() 方法?
手写生产者消费者模型

← 为什么我们不能直接调用 run() 方法? 手写生产者消费者模型→

最近更新
01
电商-商品系统设计
12-17
02
关于如何写OKR
12-09
03
对事不对人 vs 对人不对事
12-09
更多文章>
Theme by Vdoing | Copyright © 2022-2023 YoungAnnn | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式