java多线程-线程的状态及主要转化方法-4

java线程的6种状态

   // 线程的6种状态
 public static enum State {
     
        NEW, // 新建
        RUNNABLE, // 可运行
        BLOCKED, // 被阻塞
        WAITING, // 等待
        TIMED_WAITING, //  计时等待
        TERMINATED; // 被终止
    }

NEW

        NEW状态的线程表示此时尚未启动,即还没调用Thread实例的start()方法

public class TestThread {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
        });
        System.out.println(thread.getState());
    }
}

关于start()问题:

        能否反复调用同一个线程的start()方法?

        一个线程执行完毕(此时处于TERMINATED状态),再次调用这个线程的start()方法是否可行?

// start源码
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();点进去有一个native的start0()方法,通过debug start方法后发现在调用一次start()之后,threadStatus的值会改变(threadStatus !=0),此时再次调用start()方法会抛出IllegalThreadStateException异常。所以,前面的两个问题都是不可以的。

RUNNABLE

        RUNNABLE状态的线程表示线程正在java虚拟机中运行,也有可能在等待CPU分配资源,相当于传统操作系统的ready与runnning状态。当有多个RUNNABLE线程时,会使用最多 CPU 时间的那个(如果支持此功能)。 

BLOCKED

        阻塞状态。处于BLOCKED状态的线程正等待锁的释放以进入同步区。

举个例子:就好比家里的厕所,只有厕所里没人你才能进入方便(一般情况下)。

WAITING

        等待状态。由WAITING->RUNNABLE需要其他线程唤醒。

线程进入等待状态的三种方法:

  • Object.wait():使当前线程处于等待状态直到另一个线程唤醒它;
  • Thread.join():等待线程执行完毕,底层调用的是Object实例的wait方法;
  • LockSupport.park():除非获得调用许可,否则禁用当前线程进行线程调度。

        举个例子:当厕所里面人出来了,你准备进去了,但是你朋友说他憋不住了他要先方便,此时你不得不释放掉锁,让你朋友先进去方便,此时你进入WAITING状态,你朋友进入RUNNABLE状态,只有你朋友进行notify()或者notifyAll(),你才能解除WAITING状态。


TIMED_WAITING

        超时等待状态。线程等待一个具体的时间,时间到后会被自动唤醒。

调用如下方法会使线程进入超时等待状态:

  • Thread.sleep(long millis):使当前线程睡眠指定时间;
  • Object.wait(long timeout):线程休眠指定时间,等待期间可以通过notify()/notifyAll()唤醒;
  • Thread.join(long millis):等待当前线程最多执行millis毫秒,如果millis为0,则会一直执行;
  • LockSupport.parkNanos(long nanos): 除非获得调用许可,否则禁用当前线程进行线程调度指定时间;
  • LockSupport.parkUntil(long deadline):同上,也是禁止线程进行调度指定时间;

        举个例子:你到了厕所前接到一个电话让你3分钟下楼去拿个快递,你下楼等了3分钟,这3分钟你将处于TIMED_WATING,时间一过,你发现快递小哥没来,放你鸽子。你直接回去准备上厕所(争夺锁)。

TERMINATED

        终止状态。表示此时线程已执行完毕。

线程状态的转换

 BLOCKED与RUNNABLE状态的转换

public class TestThread {
    @Test
    public void blockedTest() {
        Thread a = new Thread(new Runnable() {
            @Override
            public void run() {
                testMethod();
            }
        },"a");

        Thread b  = new Thread(new Runnable() {
            @Override
            public void run() {
                testMethod();
            }
        },"b");
        a.start();
        b.start();
        System.out.println("线程名字:"+a.getName()+"线程状态:"+a.getState());
        System.out.println("线程名字:"+b.getName()+"线程状态:"+b.getState());
    }
    //同步方法竞争锁资源
    private synchronized void testMethod() {
        try {
            Thread.sleep(2000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

执行结果:

         我们可能认为程a会先调用同步方法,然后进行sleep,进入TIMED_WAITING状态,线程b则等待线程a进入BLOCKED状态

         其实不然,有两点需要值得大家注意,一是在测试方法blockedTest()内还有一个main线程,二是启动线程后执行run方法还是需要消耗一定时间的

        测试方法的main线程只保证了a,b两个线程调用start()方法(转化为RUNNABLE状态),如果CPU执行效率高一点,还没等两个线程真正开始争夺锁,就已经打印此时两个线程的状态(RUNNABLE)了。

        当然,如果CPU执行效率低一点,其中某个线程也是可能打印出BLOCKED状态的(此时两个线程已经开始争夺锁了)。也就是我上图的测试结果。

        那么,如何打印出BLOCED状态呢?BLOCKED状态首先需要两个线程去进行锁竞争。我们可以通过处理下测试方法里的main线程,调用一下Thread.sleep()方法。

        这里需要注意的是main线程休息的时间,要保证在线程争夺锁的时间内,不要等到前一个线程锁都释放了你再去争夺锁,此时还是得不到BLOCKED状态的。

        

public class TestThread {
    @Test
    public void blockedTest() throws InterruptedException{
        Thread a = new Thread(new Runnable() {
            @Override
            public void run() {
                testMethod();
            }
        },"a");

        Thread b  = new Thread(new Runnable() {
            @Override
            public void run() {
                testMethod();
            }
        },"b");
        a.start();
        Thread.sleep(1000L);
        b.start();
        System.out.println("线程名字:"+a.getName()+"线程状态:"+a.getState());
        System.out.println("线程名字:"+b.getName()+"线程状态:"+b.getState());
    }
    //同步方法竞争锁资源
    private synchronized void testMethod() {
        try {
            Thread.sleep(2000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

         在这个例子中,两线程状态如下:

  • a的状态转换过程:RUNNABLE(a.start()) -> TIMED_WATING(Thread.sleep())->RUNABLE(sleep()时间到)->BLOCKED(未抢到锁) -> TERMINATED
  • b的状态转换过程:RUNNABLE(b.start()) -> BLOCKED(未抢到锁) ->TERMINATED

        多次测试后,输出的结果可能会有所不同。

WAITING状态与RUNNABLE状态的转换

        使线程从RUNNABLE状态转为WAITING状态有三种方法 Object.wait(); Thread.join(); LockSupport.park(),下面介绍一下常用的两种方式。

Object.wait()

        调用wait()方法前线程必须持有对象锁,调用后会释放当前锁,直到有其他线程调用notify()/notifyAll()方法唤醒等待锁的线程。但有多个线程等待这个锁的时候,调用notify()不一定能够唤醒之前调用wait()那个线程,调用notifyAll()唤醒所有等待锁的线程时,时间片也不一定会把时间片分给刚才那个线程,这和系统的调度有关。

Thread.join()

        调用join()方法,会一直等待这个线程执行完毕,即转换为TERMINATED。

public class TestThread {
    @Test
    public void blockedTest() throws InterruptedException{
        Thread a = new Thread(new Runnable() {
            @Override
            public void run() {
                testMethod();
            }
        },"a");

        Thread b  = new Thread(new Runnable() {
            @Override
            public void run() {
                testMethod();
            }
        },"b");
        a.start();
        a.join();
        b.start();
        System.out.println("线程名字:"+a.getName()+"线程状态:"+a.getState());
        System.out.println("线程名字:"+b.getName()+"线程状态:"+b.getState());
    }
    //同步方法竞争锁资源
    private synchronized void testMethod() {
        try {
            Thread.sleep(2000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

执行结果为:

        其中,a线程调用start()方法后立即调用a.join(),所以main()线程会等待a线程执行完毕,至于b线程如果b线程尚未进入同步方法竞争锁资源那么将会处于RUNNABLE状态,如果进入了同步方法则会打印TIMED_WAITING状态。

TIMED_WAITING与RUNNABLE状态转换

         使线程从RUNNABLE状态转为TIMED_WAITING状态有五种方法,包括Thread.sleep(long);Object.wait(long);Thread.join(long);LockSupport.parkNanos();LockSupport.parkUntil(),这里介绍三种常用的。

Thread.sleep(long)

        线程进入传参毫秒休眠,释放执行权不释放锁,休眠时间一到重新进入RUNNABLE状态。

Object.wait(long)

        wait(long)方法使线程进入TIMED_WAITING状态。可通过其他线程调用notify()或notifyAll()方法来唤醒。

       wait(long)方法与wait()方法的区别是, wait(long)方法即使不通过其他线程调用notify()或notifyAll()方法唤醒,到达时间也会自动唤醒,去争夺锁。

Thread.join(long)

        

public class TestThread {
    @Test
    public void blockedTest() throws InterruptedException{
        Thread a = new Thread(new Runnable() {
            @Override
            public void run() {
                testMethod();
            }
        },"a");

        Thread b  = new Thread(new Runnable() {
            @Override
            public void run() {
                testMethod();
            }
        },"b");
        a.start();
        a.join(1000L);
        b.start();
        System.out.println("线程名字:"+a.getName()+"线程状态:"+a.getState());
        System.out.println("线程名字:"+b.getName()+"线程状态:"+b.getState());
    }
    //同步方法竞争锁资源
    private synchronized void testMethod() {
        try {
            Thread.sleep(3000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

执行结果为:

          a线程进行join的时候小于a线程进行sleep的时间,所以a线程的状态为TIMED_WAITING

b线程状态可能为RUNNABLE或BLOCKED

线程中断

       有些时候,当线程启动后并不需继续执行,需要进行线程中断,最初版本jdk中Thread类曾提供了一个线程终止的方法stop(),但是由于数据一致性问题,此暴力方法已经被废弃。目前java里还没有安全直接的方法来中断线程,但是java提供了线程中断机制来处理需要中断线程的情况。通过中断操作并不是直接的进行线程中断,而是通知需要被中断的线程自行处理。

    @Deprecated(
        since = "1.2"
    )
// stop方法源码
    public final void stop() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            this.checkAccess();
            if (this != currentThread()) {
                security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
            }
        }

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

        this.stop0(new ThreadDeath());
    }

中断方法

  • Thread.interrupt():中断线程。这里的中断线程并不会立即停止线程,而是设置线程的中断状态为true(默认是flase);
  • Thread.currentThread().isInterrupted():测试当前线程是否被中断。线程的中断状态受这个方法的影响,意思是调用一次使线程中断状态设置为true,连续调用两次会使得这个线程的中断状态重新转为false;
  • Thread.isInterrupted():测试当前线程是否被中断。与上面方法不同的是调用这个方法并不会影响线程的中断状态。

public class InterruptedDemo {
 
    public static void main(String[] args) {
        Thread t1 = new Thread() {
 
            @Override
            public void run() {
                while(true) {
                    System.out.println("The thread is waiting for interrupted!");
                    //中断处理逻辑
                    if(Thread.currentThread().isInterrupted()) {
                        System.out.println("The thread is interrupted!");
                        break;
                    }
                    //Thread.yield();
                }
            }
        };
        t1.start();
        t1.interrupt();//中断线程
        //System.out.println("The Thread is interrupted!");
    }
}

        在线程中断机制里,当其他线程通知需要被中断的线程后,线程中断的状态被设置为true,但是具体被要求中断的线程要怎么处理,完全由被中断线程自己而定,可以在合适的实际处理中断请求,也可以完全不处理继续执行下去。

        

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

那山川

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

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

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

打赏作者

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

抵扣说明:

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

余额充值