Thread 源码

本文深入解析Java线程的各种方法和属性,包括Thread类的构造函数、start、run、sleep、yield、interrupt、join等方法的使用及注意事项,帮助读者理解线程的生命周期和管理。

    此处只讨论,Thread中的所有public方法。

    其中,可以分为以下两种:

静态static方法在调用时,只能对当前的线程进行操作,具体用法都是:Thread.方法名()

非静态方法在调用时,不仅可以通过 Thread.currentThread().方法名() 来对当前线程进行操作;还可以直接通过Thread类的实例进行调用。

例如:

    Thread t1=new Thread(sleepDemo);

    t1.start();

    t1.方法名();

 

Thread.sleep()的用法:
package com.thread;

/**
 * @Description:
 */
public class SleepDemo implements Runnable{

    @Override
    public void run() {
        while (true){
            try {
                Thread.sleep(1000);
                System.out.println("当前线程:"+Thread.currentThread().getName()+"休眠1秒");
                Thread.currentThread().sleep(1000);
                System.out.println("当前线程:"+Thread.currentThread().getName()+"休眠1秒");
            } catch (InterruptedException e) {
                System.out.println("当前线程:"+Thread.currentThread().getName()+"休眠时被中断");
                System.out.println("当前中断状态为:"+Thread.currentThread().isInterrupted());
                break;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SleepDemo sleepDemo=new SleepDemo();
        Thread t1=new Thread(sleepDemo);
        t1.setName("sleepDemo");
        t1.start();
        System.out.println("当前线程:"+Thread.currentThread().getName());
        t1.sleep(50);
        System.out.println("注意:t1.sleep仍然是main线程进入休眠了");
        Thread.sleep(2000);
        t1.interrupt();
    }
}

运行结果:

当前线程:main
注意:t1.sleep仍然是main线程进入休眠了
当前线程:sleepDemo休眠1秒
当前线程:sleepDemo休眠1秒
当前线程:sleepDemo休眠时被中断
当前中断状态为:false

总结:

    Thread.sleep(1000);  与  Thread.currentThread().sleep(1000); 等价,都是在何处调用,就会使得当前的线程进入休眠状态。因此,t1.sleep(50); 同样是main线程休眠了50毫秒。

    sleep()支持中断,因此当检测到中断信号时,会抛出InterruptedException 异常,从而告知程序该线程应当对中断进行某些响应操作。通过isInterrupted()可以发现:在抛出异常的时候,还让异常状态重新置为了false。

    需要注意:由于不允许不同的线程之间的异常溢出,因此在run方法中就必须对此处抛出的InterruptedException异常进行处理,因此上述示例代码,直接对异常进行了捕捉。若是在其他的普通方法中,除非是线程需要退出,否则都不应当直接将异常生吞。此时可以选择:1.不捕捉 InterruptedException,将它传播给调用者;2.捕捉InterruptedException执行必要的清理操作,重新向上抛出InterruptedException异常;3.捕捉 InterruptedException 后重新恢复中断状态。

    sleep方法不会释放锁。如果当前线程持有对某个对象的锁,则即使调用了sleep方法,其他线程也无法获得这个对象的锁。

 

Thread.currentThread()的用法:

    Thread.currentThread()返回对当前正在执行的线程对象的引用。换句话说, Thread.currentThread() 返回的是一个实例。这个实例是当前Thread 的引用

 

Thread.yield()的用法:

    yield方法的作用是对线程调度器的一种建议:当前线程已执行完成重要的部分,现在时切换给其他线程执行一段时间的好时机。不过不能指定暂停的时间,并且也不能保证当前线程马上停止。因为有可能在下一个时间片当前线程再次被选中然后继续执行。

    yield只能使同优先级或更高优先级的线程有执行的机会,但是不能保证。所有对于很严格的控制时,不能依赖于yield().

调用实例:

在需要执行让步操作的线程中直接调用(如:run方法中):

    Thread.yield();

需要注意:t1.yield();与Thread.yield(); 等价 都是将当前线程抢占权让出。t1.yield(); 无法使得当前线程控制其他线程 t1 交出抢占权。

 yield方法不会释放锁。如果当前线程持有对某个对象的锁,则即使调用了yield方法,其他线程也无法获得这个对象的锁。

 

Thread.interrupted()的用法:

    返回中断状态值,并重置线程的中断状态为false.

调用实例:

    获取当前线程的中断状态(适用于非阻塞方法或不支持响应中断的阻塞方法):

        while (!Thread.interrupted()){

            System.out.println("aaaaa");

        }

    需要注意:t1.interrupted();与Thread.interrupted(); 等价 都是获得当前线程的中断状态。t1.interrupted(); 无法获得其他线程 t1 的中断状态。

 

start()的用法:

    源码:

public synchronized void start() {
    // 线程不能重复start,否则会抛出IllegalThreadStateException
    if (threadStatus != 0)
        throw new IllegalThreadStateException();
    //添加到线程组,启动数加1,启动失败数减1
    group.add(this);

    boolean started = false;
    try {
        start0();//启动线程的native方法
        started = true;//启动成功
    } finally {
        try {
            if (!started) {//若启动失败
                group.threadStartFailed(this);//从线程组移除,启动失败数加1
            }
        } catch (Throwable ignore) {
            /* do nothing. If start0 threw a Throwable then
              it will be passed up the call stack */
        }
    }
}

    一个线程启动另一个线程执行的唯一方法。并且同一个线程不能多次start,否则会抛出IllegalThreadStateException异常。

    调用实例:

Thread t1=new Thread(sleepDemo);

t1.start();

 

run()的用法:

    源码:

@Override //实现了Runnable的run方法
public void run() {
   if (target != null) {
      target.run();
   }
}

    target为Runnable成员变量实例,可以发现若直接执行run方法,并没有执行启动另一个线程的相关操作,因此执行的仍然是执行run方法的那个线程而已,等同于执行了另一个实例对象中方法名为run的普通方法。

    需要注意:target对象的初始化时在Thread的构造方法中完成的。

 

Thread的构造函数:
public Thread() {//无参构造器
    init(null, null, "Thread-" + nextThreadNum(), 0);//初始化线程名称为'Thread-'加当前线程数,线程栈大小为0
}

//Runnable target 实现了Runnable接口的类的实例
public Thread(Runnable target) {
    init(null, target, "Thread-" + nextThreadNum(), 0);//初始化线程名称为'Thread-'加当前线程数,线程栈大小为0
}

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

//ThreadGroup group 当前建立的线程所属的线程组.如果不指定线程组,所有的线程都被加到一个默认的线程组中
//Runnable target 实现了Runnable接口的类的实例
public Thread(ThreadGroup group, Runnable target) {
    init(group, target, "Thread-" + nextThreadNum(), 0);//初始化线程名称为'Thread-'加当前线程数,线程栈大小为0
}

//String name 线程的名字
public Thread(String name) {
    init(null, null, name, 0);//初始化线程栈大小为0
}

//ThreadGroup group 当前建立的线程所属的线程组.如果不指定线程组,所有的线程都被加到一个默认的线程组中
//String name 线程的名字
public Thread(ThreadGroup group, String name) {
    init(group, null, name, 0);//初始化线程栈大小为0
}

//Runnable target 实现了Runnable接口的类的实例
//String name 线程的名字
public Thread(Runnable target, String name) {
    init(null, target, name, 0);//初始化线程栈大小为0
}

//ThreadGroup group 当前建立的线程所属的线程组.如果不指定线程组,所有的线程都被加到一个默认的线程组中
//Runnable target 实现了Runnable接口的类的实例
//String name 线程的名字
public Thread(ThreadGroup group, Runnable target, String name) {
    init(group, target, name, 0);//初始化线程栈大小为0
}

//ThreadGroup group 当前建立的线程所属的线程组.如果不指定线程组,所有的线程都被加到一个默认的线程组中
//Runnable target 实现了Runnable接口的类的实例
//String name 线程的名字
//long stackSize 线程栈的大小
public Thread(ThreadGroup group, Runnable target, String name, long stackSize) {
    init(group, target, name, stackSize);
}

 

interrupt()的用法:

    不意味着立即停止目标线程的执行,而是传递了中断的消息请求。设置线程的中断状态为true,其初始状态为false。

    中断的含义:提前结束或取消目标线程的执行。

    对于支持响应中断的阻塞方法(sleep、join、wait等),当检测到中断请求时,将抛出异常。通过对异常的处理,可以很好地对中断做出响应。

    对于不支持响应中断的阻塞方法,可能会一直无法检测到中断请求,直至阻塞方法执行完毕。

    对于非阻塞方法,可以通过轮询查看中断状态值,对中断做出响应。

调用实例:

中断当前线程执行(任何方法均可这样使用):

    Thread.currentThread().interrupt();

中断其他线程的执行(在创建其他线程的方法中执行中断其他线程的操作):

    Thread t1=new Thread(sleepDemo);

    t1.start();

    t1.interrupt();

 

isInterrupted()的用法:

    判断线程是否已经中断,不对线程中断状态作任何改变。

    如果该线程已经中断,则返回 true;否则返回 false

    需要注意:如果存在可响应中断的阻塞方法,当抛出异常的同时会将中断状态重新置为false。

调用实例:

判断当前线程是否中断:

    Thread.currentThread().isInterrupted();

判断其他线程是否中断:

    Thread t1=new Thread(sleepDemo);

    t1.start();

    t1.interrupt();

    System.out.println("t1中断状态:"+t1.isInterrupted());

 

 

join()的用法:

    当前线程等待另一线程的执行。如果当前线程的执行过程中检测到中断信号,则抛出异常并将中断状态重置。

join() 等待该线程终止

join(long millis)  等待该线程终止的时间最长为 millis 毫秒。超时为0 则意味着要一直等下去

join(long millis,int nanos) 等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒

    作用是父线程等待子线程执行完成后再执行。换句话说就是将异步执行的两个线程合并为一个同步执行的线程。

    需要注意:正确的使用,只有当前线程等待另一线程执行。有参数,则只等待指定的时间;无参数,则等待另一线程执行完成方可继续当前线程的执行。join方法是一个支持中断响应的阻塞方法,因此当前线程在等待过程中, 只有有线程中断了当前线程,则当前线程会抛出InterruptedException异常,并将当前线程的中断状态重置。

调用实例:

禁止这样使用)当前线程等待当前线程的执行,当前线程将一直挂起,无法继续执行,直至接受到中断信号才能做出响应:

    Thread.currentThread().join();

当前线程等待其他线程的执行:

    Thread t1=new Thread(sleepDemo);

    t1.start(); 

    t1.join(2000); //当前线程等待线程 t1 执行2秒

    t1.join();  //当前线程等待线程 t1 执行完毕

 

isAlive()的用法:

    测试线程是否处于活动状态。如果线程已经启动且尚未终止,则为活动状态。如果该线程处于活动状态,则返回 true;否则返回 false

    调用实例:

    测试当前线程的活动状态(无意义,返回结果一定为true):

        Thread.currentThread().isAlive();

    测试其他线程的活动状态:

        Thread t1=new Thread(sleepDemo);

        t1.start();

        Thread.sleep(2000);

        t1.isAlive();

 

stop()的用法:

    强制线程停止执行。(不推荐使用!

    调用实例:

强制停止当前线程执行(任何方法均可这样使用):

    Thread.currentThread().stop();

强制停止其他线程的执行(在创建其他线程的方法中执行停止其他线程的操作):

    Thread t1=new Thread(sleepDemo);

    t1.start();

    ... ...

    t1.stop();

 

suspend()的用法:

    暂停线程,但并不释放资源。(不推荐使用)

    该方法已经遭到反对,因为它具有固有的死锁倾向。

        如果目标线程挂起时在保护关键系统资源的监视器上保持有锁,则在目标线程重新开始以前任何线程都不能访问该资源。

        如果重新开始目标线程的线程想在调用 resume 之前锁定该监视器,则会发生死锁。这类死锁通常会证明自己是“冻结”的进程。

调用实例:

暂停当前线程执行(任何方法均可这样使用):

    Thread.currentThread().suspend();

暂停其他线程的执行(在创建其他线程的方法中执行暂停其他线程的操作):

    Thread t1=new Thread(sleepDemo);

    t1.start();

    ... ...

    t1.suspend();

 

resume()的用法:

    只与suspend()一起使用,用于重新开始挂起的进程。(不推荐使用)

    如果线程处于活动状态但被挂起,则它会在执行过程中重新开始并允许继续活动。

    只与suspend()一起使用,但 suspend() 已经遭到反对。

调用实例:

恢复当前线程的执行(任何方法均可这样使用):

    Thread.currentThread().suspend();

    Thread.currentThread().resume();

恢复其他线程的执行(在创建其他线程的方法中执行恢复其他线程的操作):

    Thread t1=new Thread(sleepDemo);

    t1.start();

    ... ...

    t1.suspend();

    t1.resume();

 

setPriority()的用法:

    设置线程的优先级。(优先级越高的线程越可能先执行)

    源码:

    public final void setPriority(int newPriority) {
        ThreadGroup g;
        checkAccess();
        if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
            throw new IllegalArgumentException();
        }
        if((g = getThreadGroup()) != null) {
            if (newPriority > g.getMaxPriority()) {
                newPriority = g.getMaxPriority();
            }
            setPriority0(priority = newPriority);
        }
    }

虽然拥有MIN_PRIORITY 到 MAX_PRIORITY 的之间的10个优先级,但是它与操作系统之间的映射关系不是太好。

一般情况下,最好只使用以下三个级别作为优先级级别:

    MIN_PRIORITY = 1;

    NORM_PRIORITY = 5;

    MAX_PRIORITY = 10;

调用实例:

设置当前线程的优先级:

    Thread.currentThread().setPriority(Thread.MIN_PRIORITY);

设置其他线程的优先级:

    Thread t1=new Thread(sleepDemo);

    t1.setPriority(Thread.MIN_PRIORITY);

    t1.start();

 

getPriority()的用法:

    获得线程的优先级。

    调用实例:

获得当前线程的优先级:

    Thread.currentThread().getPriority();

获得其他线程的优先级:

    Thread t1=new Thread(sleepDemo);

    t1.start();

    t1.getPriority();

 

setName()的用法:

    设置线程的名字。

    源码:

   public final synchronized void setName(String name) {
        checkAccess();
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;
        if (threadStatus != 0) {
            setNativeName(name);
        }
    }

    注意:在调用start方法前后都可以使用setName设置线程名,但在调用start方法后使用setName修改线程名,会产生不确定性,也就是说可能在run方法执行完后才会执行setName。如果在run方法中要使用线程名,就会出现虽然调用了setName方法,但线程名却未修改的现象。

调用实例:

设置当前线程的名字:

    Thread.currentThread().setName("线程名");

设置其他线程的名字:

    Thread t1=new Thread(sleepDemo);

    t1.setName("线程名");

    t1.start();

 

getName()的用法:

    获得线程名称。

    调用实例:

获得当前线程的名字:

    Thread.currentThread().getName("线程名");

获得其他线程的名字:

    Thread t1=new Thread(sleepDemo);

    t1.start();

    String name=t1.getName("线程名");

    System.out.println("线程名:"+name);

 

setDaemon(boolean on) 的用法:

    将该线程标记为守护线程或用户线程。

        on为true:标记为守护(后台)线程;

        on为false:标记为用户线程;

    默认为false,该方法必须在启动线程前调用,才能设置为后台线程。

    当所有的非后台线程结束时,程序就终止了,同时会杀死进程中所有的后台线程。

    执行main的线程就是一个非后台线程。如果使用一个后台程序创建线程,则它创建的任何线程都将自动设置为后台线程。

    因此如果在main方法中启动一个后台线程,则往往会出现main执行完毕,程序会立即退出,而不会等待后台线程执行完毕。

调用实例:

   Thread t1=new Thread(sleepDemo);

   t1.setDaemon(true);//设置为后台线程。

    t1.start();

 

isDaemon()的用法:

    判断该线程是否为守护线程。如果该线程是守护线程,则返回 true;否则返回 false。

    调用实例:

判断当前线程是否为守护线程:

    Thread.currentThread().isDaemon();

判断其他线程是否为守护线程:

   Thread t1=new Thread(sleepDemo);

    t1.setDaemon(true);//设置为后台线程

    t1.start();

    t1.isDaemon();//检查t1是否为守护线程。    

 

activeCount()的用法:

    返回当前线程的线程组中活动线程的数目。

 

getThreadGroup()的用法:

    返回该线程所属的线程组ThreadGroup,如果该线程已经终止(停止运行),该方法则返回 null。

转载于:https://my.oschina.net/langwanghuangshifu/blog/2874296

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值