Thread类的用法

目录

    • 1. Thread类的构造方法
    • 2. Thread的几个常见属性
    • 3. Thread类中start和run的区别(经典面试题)
    • 4. 中断一个线程
      • 4.1 给线程设定一个结束标志位isQuit(自己创建的变量控制循环)
      • 4.2 Thread类内置了一个isInterrupted标志位
    • 5. 等待一个线程
    • 6. 线程的状态(6个状态)

1. Thread类的构造方法

Thread类是JVM用来管理线程的一个类,每个线程都有唯一的Thread对象与之关联。也就是说,Java代码中的Thread对象和操作系统中的线程是一一对应的

方法 说明
Thread() 创建线程对象
Thread(Runnable target) 使用Runnable对象创建线程对象
Thread(String name) 创建线程对象,并命名
Thread(Runnable target, String name) 使用Runnable对象创建线程对象,并命名
//给线程命名
public class ThreadDemo6 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
           while (true) {
               System.out.println("ttt");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        },"t线程");
        t.start();
    }
}

使用jconsole查看,发现该线程名不再是thread-0,而是t线程。
Thread类的用法_第1张图片
但是我们发现没有main线程了,因为main线程执行的太快,已经结束了。
main线程执行结束,意味着main方法执行完了。

2. Thread的几个常见属性

  1. 是否存活 isAlive()
public class ThreadDemo7 {
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            System.out.println("t");
        });
        thread.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(thread.isAlive());
    }
}

运行结果:
Thread类的用法_第2张图片
分析:上述t线程没有进行任何循环和sleep,意味着里面的代码会迅速执行完毕,main线程如果slwwp结束,此时t基本上就是已经执行完了的状态,那么t对象还在,但是在系统中对应的线程已经结束了
2. 是否为后台线程 isDaemon()

public class ThreadDemo8 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            while (true) {

            }
        });
        //默认是前台进程,也就是设置为false
        //前台进程会阻止进程结束
        t.setDaemon(false);
        t.start();
    }
}

运行结果:(没有结束)
Thread类的用法_第3张图片

分析:
默认是前台进程,也就是设置为false,前台进程会阻止进程结束。
前台线程会阻止java进程结束,必须得java进程中所有得前台线程都执行完,java进程才能结束。
后台线程不会影响到java进程的结束。
但是改成后台进程,运行(进程结束):

        t.setDaemon(true);

Thread类的用法_第4张图片

3. Thread类中start和run的区别(经典面试题)

①start方法表示(创建新的线程):创建一个线程,新的线程将会执行run方法;
②run方法表示(线程启动之后要执行的逻辑):线程的入口方法,线程启动起来要执行哪些逻辑,不是让程序员调用的,而是交给系统去自动调用的。

4. 中断一个线程

本质上说,让一个线程终止德办法就一种:让该线程德入口方法执行完毕。

4.1 给线程设定一个结束标志位isQuit(自己创建的变量控制循环)

public class ThreadDemo8 {
    public static boolean isQuit = false;
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            while (!isQuit) {
                System.out.println("ttt");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("t线程终止");
        });
        t.start();
        //在主线程中修改isQuit
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        isQuit = true;
    }
}

运行结果:
Thread类的用法_第5张图片
分析:通过循环变量来控制线程结束,当前的变量是一个成员变量。

思考:如果把isQuit从成员变量改成局部变量main该代码是否能正常工作?
不能,
不是因为作用域的关系,因为线程和线程之间共用一个内存空间,即线程1搞的变量线程2也能访问;
因为lambda变量捕获,lambda表达式能否访问外面的局部变量?(可以)
但是java要求变量捕获,捕获的变量必须是final或者“实际final(变量没有用final修饰,代码中也没有进行修改)”,因此使用成员变量来处理。

4.2 Thread类内置了一个isInterrupted标志位

public class ThreadDemo9 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            //currentThread()是获取到当前线程实例,此处currentThread得到的对象就是t
            //isInterrupted就是t对象里自带的标志位
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("ttt");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //把t内部的标志位设置成true
        t.interrupt();
        //①设置标志位为true;②如果该线程正在阻塞中(比如在执行sleep),就会把阻塞状态唤醒。通过抛出异常的方式让sleep立即结束。
        //当sleep被唤醒的时候会自动把isinterrupted标志位给清空(true->false),这就导致下次循环仍然能够执行
    }
}

运行结果:
Thread类的用法_第6张图片
分析:
①Thread.currentThread().isInterrupted():Thread.currentThread()表示获取当前的线程实例t,isInterrupted()返回的是当前线程t的状态标志位
②t线程打印3秒后,继续进行sleep,主线程的 t.interrupt();开始执行,将标志位isInterrupted()设置为true,此时t线程在sleep阻塞,那么就会唤醒它,如果sleep被唤醒,sleep会清空标志位,即把currentThread()设置为false,从而导致循环可以继续进行。
③sleep执行的时候,如果isInterrupted()标志位为false,那么sleep正常进行休眠操作;如果isInterrupted()标志位为true,那么会发生2件事:1.立即抛出异常,被唤醒;2、清空标志位为false。
解决办法:此时要终止线程,那么则需要在抛出异常的时候加上break语句。

            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("ttt");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    break;
                }
            }

为啥sleep要清空标志位?
目的就是为了让线程自身能够对于线程何时结束有一个更明确的控制。
当前interrupt方法的效果不是让线程立即结束,而是告诉线程你该结束了。
至于线程是否真的要结束,立即结束还是等会结束,都是由代码来灵活控制的,即interrupt只是通知,而不是命令。
为啥java不设置成“命令结束”,因为这种设定特别不友好,线程t何时结束,一定是t自己最清楚,交给t自身来决定比较好。

5. 等待一个线程

public class ThreadDemo10 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            System.out.println("t");
        });
        t.start();
        System.out.println("m");
    }
}

分析:上述代码无法确定到底是先输出m还是先输出t。
但是这个代码实际执行的时候,大部分情况下都是先输出m,因为创建线程t也有开销,但是不排序特定情况下,主线程的m没有执行到。
因此有时候需要明确规定线程的结束顺序——线程等待实现。join方法

public class ThreadDemo10 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            System.out.println("t");
        });
        t.start();
        try {
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("m");
    }
}

运行结果:
Thread类的用法_第7张图片
分析:在main线程中调用t.join,意思是让main线程等待t先结束,再往下执行。【如果是t1线程中调用t2.join,即让t1线程等待t2先结束】,在t.join执行的时候,如果t线程还没结束,那么main线程就会阻塞等待,其他的线程不受影响。
join的无参数版本:一直等,不见不散。
join的有参数版本:指定最大超过时间,如果等待的时间达到了上限还没等到,那就不能等了。

6. 线程的状态(6个状态)

操作系统里的线程自身是有一个状态的,但是Java的Thread是对系统线程的封装,把这里的状态又进一步的精细化了。
Thread类的用法_第8张图片

①NEW:系统中的线程还没有创建出来,只是有个Thread对象

    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            System.out.println("ttt");
        });
        System.out.println(t.getState());
        t.start();
    }

运行结果:
Thread类的用法_第9张图片
②TERMINATED:系统中的线程已经执行完了,但是Thread对象还在

    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            System.out.println("ttt");
        });
        t.start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(t.getState());
    }

运行结果:
Thread类的用法_第10张图片
③RUNNABLE:就绪状态,即正在cpu上运行 或者 准备好随时去cpu上执行

    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            System.out.println("ttt");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        t.start();
        System.out.println(t.getState()); //t处在正在运行或者即将运行状态
    }

运行结果:
Thread类的用法_第11张图片
④TIMED_WAITING:指定时间等待,例如sleep方法

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
        Thread.sleep(2000);
        System.out.println(t.getState()); 
    }

运行结果:
Thread类的用法_第12张图片
⑤BLOCKED:表示等待锁出现的状态
⑥WAITING:使用wait方法出现的状态

你可能感兴趣的:(JavaEE,jvm,java,开发语言)