并发编程入门篇(下)

本文详细介绍了Java线程的五种状态,包括NEW、RUNNABLE、BLOCKED、WAITING和TIMED_WAITING,以及线程之间的交互方式。重点讨论了wait和sleep的区别,wait会释放锁,而sleep不会;join让其他线程等待当前线程完成;yield让当前线程暂停,但不阻塞。同时,文章阐述了如何通过interrupt方法优雅地停止线程,并分析了在阻塞状态下如何中断线程。
摘要由CSDN通过智能技术生成

java线程的状态

线程的状态包括 新建状态运行状态阻塞等待状态消亡状态。其中阻塞等待状态又分为BLOCKED, WAITINGTIMED_WAITING状态。

源码:注意看我的注释

public enum State {
        NEW,//这一个已经创建的线程,但是还没有调用start方法启动的线程所处的状态。
        
        RUNNABLE,//有可能正在运行,或者正在等待CPU资源。总体上就是当我们创建线程并且启动之后,就属于Runnable状态。
        
        BLOCKED,//阻塞状态,当线程准备进入synchronized同步块或同步方法的时候,需要申请一个监视器锁而进行的等待,会使线程进入BLOCKED状态。
        
        WAITING,//该状态的出现是因为调用了Thread.sleep(),Object.wait()或者Thread.join()或者LockSupport.park()。处于该状态下的线程在等待另一个线程 执行一些其余action来将其唤醒。

        TIMED_WAITING,//该状态和上一个状态其实是一样的,是不过其等待的时间是明确的。
        
        TERMINATED;//线程执行结束了,run方法执行结束表示线程处于消亡状态了。
    }

wait和sleep的区别

  • sleep方法:是Thread类的静态方法,当前线程将睡眠n毫秒,线程进入阻塞状态。当睡眠时间到了,会解除阻塞,进入可运行状态,等待CPU的到来。睡眠不释放锁(如果有的话)。
    public static void sleep(long millis, int nanos)
    throws InterruptedException {
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
            millis++;
        }

        sleep(millis);
    }
public static native void sleep(long millis) throws InterruptedException;
  • wait方法:是Object的方法,必须与synchronized关键字一起使用,线程进入阻塞状态,当notify或者notifyall被调用后,会解除阻塞。但是,只有重新占用互斥锁之后才会进入可运行状态。睡眠时,会释放互斥锁。
    public final void wait(long timeout, int nanos) throws InterruptedException {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos > 0) {
            timeout++;
        }

        wait(timeout);
    }
public final native void wait(long timeout) throws InterruptedException;

join和yield

  • join 方法:当前线程调用,则其它线程全部停止,等待当前线程执行完毕,接着执行。
  • yield 方法:该方法使得线程放弃当前分得的 CPU 时间。但是不使线程阻塞,即线程仍处于可执行状态,随时可能再次分得 CPU 时间。

查看线程状态方式

  • 打开target
    在这里插入图片描述
  • 右击class文件选择Open in terminal
  • 输入jps查看进程
    在这里插入图片描述
  • 输入jstack 想要查看的java程序的端口号
    在这里插入图片描述
  • 找到对应线程名的日志即可看到状态
    在这里插入图片描述

线程的停止

在正常情况下,线程执行完run方法就会自动销毁,这是不需要人为干预去结束的。
而以下情况需要人为干预

  • 线程中存在无限循环执行,比如while(true)循环
  • 线程中存在一些阻塞的操作,比如sleep、wait、join等。

stop方法可以来停止线程(但不建议使用)
原因:很可能出现线程的任务执行了一半就被强制中断,最终导致数据产生问题。这种行为类似于在linux系统中执行 kill -9类似,它是一种不安全的操作。

比较好的方式是

  • 停止接收新的请求
  • 把已经接收到的请求处理完毕
  • 停止线程

这种比较比较好的方式就可以通过interrupt方法来实现。

interrupt方法(原理就是靠标识位)

事先说明
若线程是正常运行状态,则当线程发送中断请求后,线程的中断状态被置为true,即isInterrupted()返回true

当其他线程通过调用当前线程的interrupt方法,表示向当前线程打个招呼,告诉他可以中断线程的执行了,至于什么时候中断,取决于当前线程自己。线程通过检查自身是否被中断来进行相应,可以通过isInterrupted()来判断是否被中断。

案例

public class InterruptDemo {
    private static int i; public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(()->{
            while(!Thread.currentThread().isInterrupted()){
                //默认情况下 isInterrupted返回false、通过thread.interrupt变成了true
                i++;
            }
            System.out.println("Num:"+i);
            },"interruptDemo");
        thread.start();
        TimeUnit.SECONDS.sleep(1);
        thread.interrupt();
    }
}

在这里插入图片描述
我们可以看到,线程收到中断信号后,仍然打印了数据,说明线程是把自己的任务执行完了才退出的!

这种interrupt中断操作的方式能够使线程在终止时有机会去清理资源,而不是武断地将线程停止,因此这种终止线程的做法显得更加安全和优雅。与这种方法一样道理的还有通过标识位(即false和ture),很简单就不演示了

处于阻塞状态下的线程中断

事先说明
这个跟上面不一样,处于阻塞的线程(即在执行Object对象的wait方法,或者线程类的joinsleep方法后线程的状态),当线程调用interrupt()方法后,这些方法将抛出InterruptedException异常,并清空线程的中断状态,即isInterrupted()返回false抛出异常的目的是为了线程从阻塞状态醒过来而返回false的目的是将真正结束线程的权利交给程序员。

public class BlockedDemo {
    private static int i;
    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(()->{
            while(!Thread.currentThread().isInterrupted()){
                try {
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                   // Thread.currentThread().interrupt();
                }
            }
            System.out.println("Num:"+i);
        },"interruptDemo");
        thread.start();
        TimeUnit.SECONDS.sleep(1);
        thread.interrupt();
    }
}

执行结果
在这里插入图片描述
可以看到线程只抛出了异常,并没有被中断,因为isInterrupted()返回false,会继续一直循环

如果要真正停止,需要在捕获到异常的地方再次真正中断

public class BlockedDemo {
    private static int i;
    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(()->{
            while(!Thread.currentThread().isInterrupted()){
                try {
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    Thread.currentThread().interrupt();
                    
                }
            }
            System.out.println("Num:"+i);
        },"interruptDemo");
        thread.start();
        TimeUnit.SECONDS.sleep(1);
        thread.interrupt();
    }
}

在这里插入图片描述
可以看到线程真的结束了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值