【JAVA多线程】Thread 源码分析

在这里插入图片描述

Thread

线程是程序中的执行线程。Java 虚拟机允许应用程序同时运行多个执行线程。

每个线程都有一个优先级。具有较高优先级的线程优先于具有较低优先级的线程执行。每个线程可能会也可能不会被标记为守护进程。当在某个线程中运行的代码创建一个新Thread对象时,新线程的优先级最初设置为等于创建线程的优先级,并且当且仅当创建线程是守护进程时,它才是守护线程。

有两种方法可以创建一个新的执行线程。一种是将类声明为Thread

创建线程的另一种方法是声明一个实现Runnable接口的类。

每个线程都有一个用于识别目的的名称。多个线程可能具有相同的名称。如果在创建线程时未指定名称,则会为其生成一个新名称。

常用属性

守护线程

 public final void setDaemon(boolean on) {
        checkAccess();
        if (isAlive()) {//在线程启动之前设置守护线程
            throw new IllegalThreadStateException();
        }
        daemon = on;
    }

JVM进程中没有一个非守护线程,JVM自动退出,守护线程具备自动结束生命周期的特点,例如垃圾回收就是守护线程,守护线程一般用来执行后台任务。

线程名字

public final synchronized void setName(String name) {
        checkAccess();
        this.name = name.toCharArray();//转为字符数组
        if (threadStatus != 0) {
            setNativeName(name);
        }
    }

需要注意的是线程名称只能在线程启动之前修改,线程一旦启动,线程名称不能再修改。

如果没有显示的指定线程名称,线程会使用 "Thread-"作为前缀,与nextThreadNum进行组合,生成当前线程名称。

线程优先级

priority 等级。Java线程的等级是1~10,默认是5。需要注意的是:有些系统会忽略优先级,程序的正确性不能依赖线程的优先级。

线程本地变量

ThreadLocal

父子线程

Thread.currentThread()可以获取当前线程。

ThreadGroup一个线程组代表一组线程。此外,一个线程组还可以包括其他线程组。线程组形成一棵树,其中除了初始线程组之外的每个线程组都有一个父级。

允许线程访问有关其自己的线程组的信息,但不能访问有关其线程组的父线程组或任何其他线程组的信息。

 private final ThreadGroup parent;
    Thread threads[]; // 保留的线程数组
    ThreadGroup groups[]; // 子线程组

初始化

public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);//没有设置线程名字,设置默认
    }

private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc) {
        this.name = name.toCharArray();
		//新的线程的创建是由父线程创建的,main函数所在的线程是JVM创建
        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
          
            if (security != null) {
                g = security.getThreadGroup();
            }
			//默认会分配到父线程的线程组,同时拥有和父线程同样的优先级
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }
        ...
        this.group = g;
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        ...
    }

默认情况下,子线程继承了父线程的守护线程属性、优先级、线程组等特性。

启动

 public synchronized void start() {
       
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        group.add(this);

        boolean started = false;
        try {
            start0();//这里使用了本地调用,通过C代码初始化线程需要的系统资源
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

运行

执行start后处于可运行状态

 public void run() {
        if (target != null) {
            target.run();//这里的target实际上要保存的是一个Runnable接口的实现的引用
        }
    }

所以使用继承Thread创建线程类时,需要重写run方法,因为默认的run方法什么也不干。

public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

当我们使用Runnable接口实现线程类时,为了启动线程,需要先勇该线程类实例初始化一个Thread

中断

不要使用过期的supend,resume,stop。

调用 interrupt() 方法仅仅是在当前线程中打一个停止的标记,并不是真的停止线程。

也就是说,线程中断并不会立即终止线程,而是通知目标线程,有人希望你终止。至于目标线程收到通知后会如何处理,则完全由目标线程自行决定。

 public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();

        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();           // 仅仅是在当前线程中打一个停止的标记
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }

目标线程

if(Thread.currentThread().isInterrupted()){//通过判断是否中断标志位
            //处理中断逻辑
            break;
        }

Thread.sleep() 方法会抛出一个 InterruptedException 异常,当线程被 sleep() 休眠时,如果被中断,这会就抛出这个异常。

等待通知

join

等待这个线程死掉。当我们调用某个线程的这个方法时,这个方法会挂起调用线程,直到被调用线程结束执行。

 public final synchronized void join(long millis)
    throws InterruptedException {
 		...
        if (millis == 0) {
            while (isAlive()) {//判断这个线程是否是活的
                wait(0);//此方法的调用与调用的行为方式完全相同。
            }
        } else {
         ...
        }
    }

线程退出的时候,清除状态,这个时候调用isAlive() 就会返回false。然后调用lock.notify_all(thread); 这个时候就会唤醒线程上所有的wait,然后Thread.join里面的while循环就会继续。

wait

使线程停止运行,会释放对象锁。

notify/notifyAll

通知一个在该对象上等待的线程,可以继续运行。

静态方法

yield

放弃当前的CPU资源,将它让给其他任务去占用CPU执行时间。但放弃的时间不确定,有可能刚刚放弃,马上又获得CPU时间片。

Thread.yield();//一个静态方法

获取当前线程

Thread.currentThread()

睡眠

Thread.sleep(1s);//不会释放对象锁

使睡眠中的线程停止睡眠可以interrupt进行中断

猜你喜欢

转载自blog.csdn.net/qq_15604349/article/details/124498396