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

线程创建方式

  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() 原理

源码

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案例

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就会结束运行.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,您想了解关于Java并发编程中的CountDownLatch吗? CountDownLatch是Java并发编程中非常重要的一个工具类,它可以让某个线程等待其他线程完成后再继续执行。它的作用类似于倒计时门闩,当count值减为0时,所有等待者都会被唤醒。 CountDownLatch是一次性的,也就是说,它的计数器只能使用一次。如果需要重复使用,可以考虑使用CyclicBarrier。 在使用CountDownLatch时,我们需要先创建一个CountDownLatch对象,并指定计数器的初始值。然后在需要等待的线程中调用await()方法进行等待,同时在其他线程中调用countDown()方法进行计数器的减1操作。 举个例子,假设我们有一个需求:主线程需要等待两个子线程完成后再执行。那么可以这样编写代码: ```java import java.util.concurrent.CountDownLatch; public class CountDownLatchDemo { public static void main(String[] args) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(2); new Thread(() -> { System.out.println(Thread.currentThread().getName() + "执行开始"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "执行完毕"); countDownLatch.countDown(); }, "线程1").start(); new Thread(() -> { System.out.println(Thread.currentThread().getName() + "执行开始"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "执行完毕"); countDownLatch.countDown(); }, "线程2").start(); System.out.println(Thread.currentThread().getName() + "等待子线程执行完毕"); countDownLatch.await(); System.out.println(Thread.currentThread().getName() + "所有子线程执行完毕,继续执行主线程"); } } ``` 在上面的例子中,我们首先创建了一个计数器初始值为2的CountDownLatch对象,然后创建了两个线程分别进行一些操作,并在操作结束后调用countDown()方法进行计数器减1操作。在主线程中,我们调用await()方法进行等待,直到计数器减为0时,主线程才会继续执行。 希望能够对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

干饭两斤半

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值