Thread中常用API

线程状态

线程从创建到最终的消亡,要经历若干个状态。一般来说,线程包括以下这几个状态:创建(new)、就绪(runnable)、运行(running)、阻塞(blocked)、消亡(dead)。

当需要新起一个线程来执行某个子任务时,就创建了一个线程。但是线程创建之后,不会立即进入就绪状态,因为线程的运行需要一些条件(比如内存资源,在前面的JVM内存区域划分一篇博文中知道程序计数器、Java栈、本地方法栈都是线程私有的,所以需要为线程分配一定的内存空间),只有线程运行需要的所有条件满足了,才进入就绪状态。

当线程进入就绪状态后,不代表立刻就能获取CPU执行时间,也许此时CPU正在执行其他的事情,因此它要等待。当得到CPU执行时间之后,线程便真正进入运行状态。

线程在运行状态过程中,可能有多个原因导致当前线程不继续运行下去,比如用户主动让线程睡眠(睡眠一定的时间之后再重新执行)、用户主动让线程等待,或者被同步块给阻塞,此时就对应着多个状态:time waiting(睡眠或等待一定的事件)、waiting(等待被唤醒)、blocked(阻塞)。

当由于突然中断或者子任务执行完毕,线程就会被消亡。

1、start方法和run方法的关系

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

        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {

            }
        }
    }

    start0是一个本地方法(JNI方法),run方法是被start0调用的。仔细阅读start方法将会有以下要点:

  1. Thread被构造后的new状态,事实上threadStatus这个内部属性为0;不能两次启动Thread,否则会报IllegalThreadStateException异常。
  2. 线程启动后将会被加入一个ThreadGroup中。
  3. 一个线程结束,再次调用start是不被允许的。

2、TimeUnit与Thread.sleep

        TimeUnit是(JUC)包下面的一个类,TimeUnit提供了可读性更好的线程暂停操作,通常用来替换Thread.sleep(),在很长一段时间里Thread的sleep()方法作为暂停java.util.concurrent线程的标准方式,几乎所有Java程序员都熟悉它,事实上sleep方法本身也很常用而且出现在很多面试中。

        如果你已经使用过Thread.sleep(),当然我确信你这样做过,那么你一定熟知它是一个静态方法,暂停线程时它不会释放锁,该方法会抛出InterrupttedException异常(如果有线程中断了当前线程)。但是我们很多人并没有注意的一个潜在的问题就是它的可读性。Thread.sleep()是一个重载方法,可以接收长整型毫秒和长整型的纳秒参数,这样对程序员造成的一个问题就是很难知道到底当前线程是睡眠了多少秒、分、小时或者天。

        java.utils.concurrent .TimeUnit是Java枚举应用场景中最好的例子之一,所有TimeUnit都是枚举实例,让我们来看看线程睡眠4分钟用TimeUnit是如何使用的。

TimeUnit.MINUTES.sleep(4);  // sleeping for 4 minutes

        类似你可以采用秒、分、小时级别来暂停当前线程。你可以看到这比Thread的sleep方法的可读的好多了。记住TimeUnit.sleep()内部调用的Thread.sleep()也会抛出InterruptException。你也可以查看JDK源代码去验证一下。

3、yield方法

    yield方法是一种启发式的方法,其会提醒调度器我愿意放弃当前的cpu资源,如果cpu的资源不紧张,则会忽视这种提醒。

与sleep方法的区别

  • sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;
  • 线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态;
  • sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常;
  • sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性。

4、线程的优先级

        在线程中,我们可以通过线程的setPriority()方法来设置线程的优先级,方法的参数是一个整型值,可以填写从1~10的值。当我们创建线程的时候,java默认给线程设置的优先级是5,1为最低优先级,10为最高优先级。下面的例子创建了a,b两个线程并给它们设置了优先级:

private final void setPriority(int newPriority);       //设置线程的优先级
private final int getPriority();       //获取线程的优先级

       我们很容易就以为,线程的优先级是线程从cpu手里抢到资源的权重,认为优先级越高的线程越容易在线程竞争中获胜,这是初学者的一个误区。优先级和线程抢到资源的概率并没有关系,而是在有多个线程的时候,用来设置一上来从哪个线程开始执行的优先级,和之后抢夺cpu的权重并无关系。

       如果制定的线程优先级大于所在线程组的优先级,那么制定的优先级将会失效,取而代之的是group的优先级。

5、interrupt方法

       使用interrupt()中断线程

6、join方法

        在很多情况下,主线程创建并启动子线程,如果子线程中要进行大量的耗时运算,主线程往往将早于子线程结束之前结束。如果主线程想等待子线程完成之后再结束,就要用到join()方法。

public class Run {
    public static void main(String[] args) {
        try{
            Object lock=new Object();
            MyThread1 t1=new MyThread1(lock);
            t1.start();
            //线程t1正常执行,而当前的main线程进行无限期的阻塞
            //等待线程t1销毁后再继续执行main线程后面的代码
            t1.join();
        }catch(InterruptedException e){
        }
    }
}

//方法join的作用是使所属的线程对象t1正常执行run()方法中的任务,而使当前线程main进行无限期的阻塞,等待线程t1销毁后(执行完成)再继续执行线程main后面的代码。

join(long) :设定等待的时间。

方法join(long)与 sleep(long) 的区别

  1. 方法join(long)的功能在内部是使用wait(long)方法来实现的,所以join(long)方法具有释放锁的特点。
  2. sleep(long)方法却不释放锁。

7、常用方法

public long getId()     //获取线程id,JVM启动会开辟很多线程,所以并不一定是从0自增。
public Static Thread currentThread()         //返回当前执行线程的引用。
private ClassLoader contextClassLoader();    //加载器,默认为AppClassLoader
public void setContextClassLoader(ClassLoader cl);    //设置线程类加载器,可以打破双亲委托机制
private boolean daemon = false;      //该线程是否为守护线程,默认为false
private Runnable target;       //该线程要执行的任务
private ThreadGroup group;     //该线程归属的线程组

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值