【JUC-1】java多线程线程基础知识

news/2024/5/20 10:19:51 标签: java, JUC, 多线程

线程创建方式

  1. 继承Thread类.
  2. 实现Runable接口.
  3. 实现Callable接口.

Runable/Callable接口的实现, 都是重写其中的run/call方法, 实现任务逻辑, 再由线程执行器(可以是Thread类,也可以是线程池)并发执行run/call的逻辑.

而Thread类中的包含start方法, 可以控制线程启动,执行任务.

线程上下文切换(Thread Context Switch)

当发生线程上下文切换时, 操作系统会保存当前线程状态, 并且恢复另一个线程的运行. java程序计数器就会记录当前线程下一个执行指令的地址, 当线程切换时, 依据这个地址继续执行程序. 这个程序计数器是线程私有的.

线程状态

NEW: 线程创建, 还没与操作系统中的线程关联

注意操作系统的线程状态定义与java中有所不同. 操作系统中的block是指, 调用了阻塞API比如IO读取文件操作, 操作系统的线程状态就会进入阻塞状态.

操作系统的可运行状态是指线程还没有获得cpu时间片

现成状态转换图:

在这里插入图片描述

Thread类中常见API

方法名说明备注
start() void启动一个线程,现成执行run方法中逻辑方法执行后, 新创建线程为就绪态
join() void阻塞等待当前线程执行结束
join(long n) void阻塞等待当前线程执行结束, 最多等待n毫秒
getId() long获取long类型线程ID
getName() String获取线程名称
getPriority() int获取线程优先级1 - 10 , 10优先级最高
setPriority(int i)设置线程优先级最大 10, 最小1, 超出抛异常
getState() State获取线程状态枚举值State:{NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED}
isInterrupted() boolean判断现成是否被中断
interrupt() void打断线程sleep, waite, join时的线程, 如果调用interrupt方法会抛异常:InterruptedException, 并且清除中断标记(isInterrupted方法返回false), 如果是运行中的线程, 被打断, 会设置打断标记(isInterrupted方法返回false)
isAlive() boolean判断现成是否存活即未运行完毕的线程
sleep(long n)线程休眠,进入time_waiting状态
yield()线程让出时间片

join() 原理

源码

java">public final synchronized void join(long millis) throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }
	
    // 如果未设置超时时间, 一直等待, 直到被其他线程唤醒
    if (millis == 0) {
        while (isAlive()) {
            wait(0);
        }
    } else {
        // 设置超时时间,加入等待
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

join案例

java">public class Test7 {
    private static final Object obj = new Object();

    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (obj) {
                    try {
                        System.out.println("执行逻辑");
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });
        
        t.start();
        t.join();
    }
}

在上面的案例代码中, t.join()方法会让main方法进入wait等待. 阻塞的是main方法. 当t1线程执行完run方法逻辑后. JVM底层就会调用notify方法, 唤醒持有t1锁对象的线程.(join()方法是被synchonized修饰的, 锁对象就是线程对象)

JVM唤醒join源码

// 位于/hotspot/src/share/vm/runtime/thread.cpp中
void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
    // ...

    // Notify waiters on thread object. This has to be done after exit() is called
    // on the thread (if the thread is the last thread in a daemon ThreadGroup the
    // group should have the destroyed bit set before waiters are notified).
    // 有一个贼不起眼的一行代码,就是这行
    ensure_join(this);

    // ...
}


static void ensure_join(JavaThread* thread) {
    // We do not need to grap the Threads_lock, since we are operating on ourself.
    Handle threadObj(thread, thread->threadObj());
    assert(threadObj.not_null(), "java thread object must exist");
    ObjectLocker lock(threadObj, thread);
    // Ignore pending exception (ThreadDeath), since we are exiting anyway
    thread->clear_pending_exception();
    // Thread is exiting. So set thread_status field in  java.lang.Thread class to TERMINATED.
    java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);
    // Clear the native thread instance - this makes isAlive return false and allows the join()
    // to complete once we've done the notify_all below
    java_lang_Thread::set_thread(threadObj(), NULL);

    // 同志们看到了没,别的不用看,就看这一句
    // thread就是当前线程,是啥?就是刚才例子中说的t线程啊。
    lock.notify_all(thread);

    // Ignore pending exception (ThreadDeath), since we are exiting anyway
    thread->clear_pending_exception();
}

守护线程

Java提供了两种线程:守护线程用户线程

当所有非守护线程结束时,程序就会终止,而不管守护线程是否还在运行。守护线程通常用于执行一些后台任务或服务,例如垃圾回收器(Garbage Collector)线程就是一个守护线程。

最主要的区别就是, 系统中某一时刻, 存在守护线程正在运行, 而没有用户线程运行. 那么JVM就会结束运行.


http://www.niftyadmin.cn/n/1268637.html

相关文章

力扣01两数之和

给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。 你可以按任意顺序返回…

Leetcode 101:对称二叉树(超详细的解法!!!)

给定一个二叉树&#xff0c;检查它是否是镜像对称的。 例如&#xff0c;二叉树 [1,2,2,3,4,4,3] 是对称的。 1/ \2 2/ \ / \ 3 4 4 3但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的: 1/ \2 2\ \3 3说明: 如果你可以运用递归和迭代两种方法解决这个问题&…

嵌套访问_解锁 Prism 8 新功能 | 嵌套T检验以及嵌套的单因素方差分析

whats new in Prism 8? - 嵌套T检验以及嵌套的单因素方差分析Prism 8为嵌套数据提供了一种新的数据表&#xff0c;其中每个子列中的值都是相关的&#xff0c;并可以创建这些数据的子列图。在这个例子中&#xff0c;三只老鼠都有两种治疗方法&#xff0c;每只老鼠的结果变量都测…

Leetcode 109:有序链表转换二叉搜索树(超详细的解法!!!)

给定一个单链表&#xff0c;其中的元素按升序排序&#xff0c;将其转换为高度平衡的二叉搜索树。 本题中&#xff0c;一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。 示例: 给定的有序链表&#xff1a; [-10, -3, 0, 5, 9],一个可能的…

夸克怎么恢复云端书签_怎么将icloud的备份恢复到手机上

当设置一部新的iPhone时&#xff0c;只需勾选【从iCloud备份中恢复】选项。然后登入个人的iCloud账户之后&#xff0c;即可选择备份进行恢复。而如果是已经使用过的旧设备&#xff0c;由于苹果的设置&#xff0c;从iCloud恢复需要先抹掉设备上的所有内容。具体介绍如下&#xf…

Leetcode 114:二叉树展开为链表(超详细的解法!!!)

给定一个二叉树&#xff0c;原地将它展开为链表。 例如&#xff0c;给定二叉树 1/ \2 5/ \ \ 3 4 6将其展开为&#xff1a; 1\2\3\4\5\6解题思路 我们先要找到root.left的最右边&#xff0c;此时为4&#xff0c;然后4->rightroot.right&#xff0c;root.rightroot…

力扣02. 两数相加

给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外&#xff0c;这两个数都不会以 0 …

themleft模板库_thymeleaf模板引擎

thymeleaf 模块引擎类似于JSP的EL表达式1.引入thymeleaforg.springframework.bootspring-boot-starter-thymeleaf3.0.11.RELEASE2.1.12.thymeleaf的使用&语法只要我们把HTML文件放在classpath:/templates&#xff1b;thymeleaf会自动渲染1)场景&#xff1a;我们controller有…