线程的状态及相关问题

        工作中大家肯定都碰到过多线程,那么对于线程的几种状态你了解吗?首先,你得弄清楚线程有几种状态,然后才是怎么对各种状态的了解。

        线程有6种状态,分别为NEW(初始),RUNNABLE(运行),WAITING(等待),TIME_WAITING(超时等待),BLOCKED(阻塞),TERMINATED(终止)。

 

一、NEW(初始)

       一个线程创建好以后就是New状态

二、RUNNABLE(运行)

       当调用线程的start()方法时线程就是Runnable,可能不太清楚的同学会说不应该是Running状态吗,其实Runnable包括Ready和Running两个状态,我们一般将这两个状态合成一个状态,即Runnable。其实Ready到Running状态的切换是根据系统调度来决定的,不是通过程序来控制的。但是Running到Ready却是可以通过程序来控制的,调用线程的yield()方法,让出当前线程占用CPU的时间片,这样当前线程的状态就是Ready状态了。

三、WAITING(等待)

       调用当前线程的wait(),join()或者调用LockSupport.park()方法也能使线程进入Waiting状态。当然调用notify()、notifyall()或者LockSupport.unpark(Thread)会将线程从Waiting状态切换到Runnable状态。

       LockSupport是java.util.concurrent.locks包下的,我们所知道的ReentrantLock就是调用LockSupport的park()方法进行挂起,也是进入到了Waiting状态。

       至于Join()方法,是将执行当前线程的主方法进行阻塞,进入Waiting状态,然后当前线程执行完毕以后,线程的销毁之前会调用notifyall()方法唤醒所有处于Waiting()状态的线程,即main方法,然后main方法继续往下执行。

public class ThreadJoin {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            System.out.println("t1 is Running");
        });
        Thread t2 = new Thread(() -> {
            System.out.println("t2 is Running");
        });
        Thread t3 = new Thread(() -> {
            System.out.println("t3 is Running");
        });

        t1.start();
        t2.start();
        t3.start();
    }
}

这样运行的话,结果t1,t2,t3出现的顺序可能各种各样,但是如果加入了join()方法

public class ThreadJoin {
    public static void main(String[] args) throws Exception{
        Thread t1 = new Thread(() -> {
            System.out.println("t1 is Running");
        });
        Thread t2 = new Thread(() -> {
            System.out.println("t2 is Running");
        });
        Thread t3 = new Thread(() -> {
            System.out.println("t3 is Running");
        });

        t1.start();
        t1.join();
        t2.start();
        t2.join();
        t3.start();
    }
}

结果发现,t1 is Running,t2 is Running,t3 is Running按照顺序打印出来了。

各位可以进入Thread中看一下join方法,

 会调用执行当前线程的主线程,也就是main的wait()方法,将main线程的状态变为timed_waiting状态,所以只有t1先执行了,那么会有人问,后面的怎么执行了呢,你们有兴趣的可以去hotspot的源码下看一下,当t1执行结束系统会调用notifyall()方法唤醒处于Timed_waiting和waiting的线程,这时候main线程就会被唤醒,继续执行t2,下面的步骤也是跟上面的一样了,所以t1,t2,t3会按照顺序执行。

四、TIMED_WAITING(超时等待)

       调用当前线程的sleep(long),wait(long),join(long)或者调用LockSupport.parkNanos()方法或LockSupport.parkUtil方法()也能使线程进入Timed_Waiting状态。当然调用notify()、notifyall()或者LockSupport.unpark(Thread)会将线程从Timed_Waiting状态切换到Runnable状态。

public class ThreadType {

    public static void main(String[] args) {

        new Thread(() -> {
            while (true){
                try {
                    Thread.currentThread().sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"TIME_WAITING").start();

        new Thread(() -> {
            while (true){
                try{
                    synchronized (ThreadType.class){
                        ThreadType.class.wait();
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        },"WAITING").start();
    }
}

 在idea的Terminal下用jps查看运行的进程

再用jstack 8468,可以看到

 名字为WAITING的进程状态为WAITING,名字为TIME_WAITING的进程状态为TIMED_WAITING

 

五、BLOCKED(阻塞)

       当对一个资源进行加Synchronized锁的时候,获取Synchronized锁的线程是处于Runnable状态的,而等待该线程释放锁的其它线程则处于Blocked状态,注意是其它等待锁的线程处于Blocked状态。

public class ThreadType {

    public static void main(String[] args) {
        new Thread(new SycClass(),"timed_waiting").start();
        new Thread(new SycClass(),"blocked").start();
    }

    static class SycClass extends Thread{
        public void run(){
            synchronized (SycClass.class){
                while (true){
                    try {
                        Thread.currentThread().sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

同上,使用jps,然后jstack+进程号,可以看到

可以看到,timed_waiting拿到了SycClass类的锁,但是由于调用了sleep(1000),导致该线程处于TIMED_WAITING,而blocked线程由于等待timed_waiting释放锁,所以一直处于z

六、TERMINATED(终止)。

       这个就很好理解了,线程运行结束就进入了Terminated状态。

七、其它问题

1.线程终止方法,我们讲的是应用层的线程终止

(1).加flag(设置一个成员变量,通过更改成员变量的变化来终止线程)

public class TerminateThread {

    private volatile static Boolean flag = true;

    public static void main(String[] args){

        Thread t1 = new Thread(() ->{
            while (flag){
                System.out.println("running");
            }
        });

        t1.start();
        flag = false;

    }

(2).使用interrupt()

public class TerminateThread {

    public static void main(String[] args){

        Thread t1 = new Thread(() ->{
            while (!Thread.currentThread().isInterrupted()){
                System.out.println("running");
            }
        });

        t1.start();
        t1.interrupt();
    }
}

调用Thread.currentThread().isInterrupted()默认返回当前线程的native方法,返回一个false,但是当调用t1.interrupt()后,再调用 Thread.currentThread().isInterrupted()会返回一个true

(3)线程的stop()方法,但是不建议使用,因为相当于kill -9,使用这个把当前的进程都干掉了,面试的时候也不能说这个方法,是丢分项。

2.线程的复位

线程复位调用Thread.interrupted()即可将终止的标识由true变成false,复位到原始状态。

所有涉及线程阻塞的地方都会出现InterruptException异常,因为可能在阻塞的时候,调用interrupt()终止线程,但是因为处于阻塞状态,所以无法终止。而且就算捕获了这个异常,也是提醒你想终止,但是因为阻塞无法终止,因此,接下来的方法还是会继续进行。

想要终止就在获取异常的地方进行自己的处理。 

3.锁的作用域

刚才提到了Synchronized方法,那么锁的作用域分为那几类呢?

(1)代码块锁

这个就很好理解了,这个就不解释了

(2)实例锁

public class SynDemo {

    public void testScope(){
        synchronized (this){
            //TODO
        }
    }

    public static void main(String[] args){
        SynDemo s1 = new SynDemo();
        SynDemo s2 = new SynDemo();

        s1.testScope();
        s2.testScope();

    }
}

 这个就是实例锁,Synchronized(this)中的this就是指当前的实例SynDemo,所以s1和s2是不同的实例,所以两者锁住的不是同一个实例,因此这样调用没有任何意义。

(3)全局锁

public class SynDemo {

    public void testScope(){
        synchronized (SynDemo.class){
            //TODO
        }
    }

    public static void main(String[] args){
        SynDemo s1 = new SynDemo();
        SynDemo s2 = new SynDemo();

        s1.testScope();
        s2.testScope();

    }
}

 这个就是全局锁了,锁住的是SynDemo这个类了,类的作用范围是整个进程,进程结束了类才被销毁,这个时候s1和s2调用testScope()就必须先获得锁,才能操作。

那么问题来了:

Integer t1 = new Integer(1);

    public void testScope(){
        synchronized (t1){
            t1++;
            System.out.println(t1);
        }
    }

这个能锁住t1吗???分范围,在-128~127之间能锁住,在这个范围外面锁不住,在 -128~127是在常量池中,是同一个实例,但是在这个范围外不是就不是从常量池中取了,而是每次重新创建,所以是不同的实例,锁不住了。

 

Integer count = new Integer(1);

Count++;

Sychronized(count)能不能锁住这个类

不能,因为Integer在-128到127是一个实例,但是在这之外是多个实例,锁不住了

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值