JavaEE--Thread 类的基本用法(不看你会后悔的嘿嘿)

Posted T7ooo3o

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaEE--Thread 类的基本用法(不看你会后悔的嘿嘿)相关的知识,希望对你有一定的参考价值。

Thread类是JVM用来管理线程的一个类,换句话说,每个线程都唯一对应着一个Thread对象.

因此,认识和掌握Thread类弥足重要.

本文将从

  1. 线程创建
  2. 线程中断
  3. 线程等待
  4. 线程休眠
  5. 获取线程实例

等方面来进行具体说明.

1)线程创建

方法1:通过创建Thread类的子类并重写run () 方法

class MyThread extends Thread
    @Override
    public void run() 
        System.out.println("thread");
    

public class Thread1 
    public static void main(String[] args) 
        Thread thread = new MyThread();
        thread.start();
        System.out.println("main");
    

方法2:通过创建Runnable接口的实现类并重写run ()方法,传实现类的对象作为构造器

class MyRunnable implements Runnable
    @Override
    public void run() 
        System.out.println("thread");
    

public class Thread2 
    public static void main(String[] args) 
        Thread thread = new Thread(new MyThread());
        thread.start();
        System.out.println("main");
    

方法3:使用匿名类,new类,也就是创建该类的子类

public class Thread3 
    public static void main(String[] args) 
        Thread thread = new Thread()
            @Override
            public void run() 
                System.out.println("thread");
            
        ;
        thread.start();
        System.out.println("main");
    

方法4:使用匿名内部类,new接口,也就是创建该接口的实现类

public class Thread4 
    public static void main(String[] args) 
        Thread thread = new Thread(new Runnable() 
            @Override
            public void run() 
                System.out.println("thread");
            
        );
        thread.start();
        System.out.println("main");
    

方法5:使用lambda表达式(日常开发中使用最多的形式)

public class Thread5 
    public static void main(String[] args) 
        Thread thread = new Thread(()->
            System.out.println("thread");
        );
        thread.start();
        System.out.println("main");

    

2)线程中断

顾名思义,也就是让线程停止.

本质上而言,让线程停止,方法就一种 -->执行完线程入口方法(run ()方法).

只不过此处可能是正常执行结束,也可能是因为异常而导致结束.

目前常见的有以下两种方式:
1.使用自定义的变量来作为标志位
2.使用Thread提供的变量来作为标志位

下面来介绍:

方法1.使用自定义的变量来作为标志位

public class ThreadDemo9 
    public static boolean isQuit = false;

    public static void main(String[] args) 
        // boolean isQuit = false;

        Thread t = new Thread(() -> 
            while (!isQuit) 
                System.out.println("hello t");
                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;
    

执行结果:

此处代码的逻辑大概就是,主线程sleep ()3秒后,t线程里的循环差不多执行3次(sleep()了3秒),此时isQUit被置为ture,t线程里循环结束.t线程继续向下执行逻辑,最终t线程正常结束.

假设:此处设置的isQuti不是类变量,而是局部变量,可行吗?

结果是显然不行的.那到底是什么原因呢?其实是因为-->lambda表达式存在变量捕获.

变量捕获,只能捕获到由1)final修饰2)实际上final的局部变量.

但是如果该局部变量的值被修改了,那么lamoda表达式就捕获不到该值了.

所以此处该如何做呢? --> 设置成类变量.也就是由static修饰的变量.

因为类变量不受变量捕获的限制.

方法2:使用Thread提供的变量来作为标志位

使用Thread.currentThreadOisInterrupted0 代替自定义标志位.

Thread 内部包含了一个 boolean 类型的变量作为线程是否被中断的标记.

public class ThreadDemo10 
    public static void main(String[] args) 
        Thread t = new Thread(() -> 
            // currentThread 是获取到当前线程实例.
            // 此处 currentThread 得到的对象就是 t
            // isInterrupted 就是 t 对象里自带的一个标志位.
            while (!Thread.currentThread().isInterrupted()) 
                System.out.println("hello t");
                try 
                    Thread.sleep(1000);
                 catch (InterruptedException e) 
                    e.printStackTrace();
                
            
        );

        t.start();

        try 
            Thread.sleep(3000);
         catch (InterruptedException e) 
            e.printStackTrace();
        
        // 把 t 内部的标志位给设置成 true
        t.interrupt();
    

 

原因如下:

首先需要明白interrupt 方法的作用:

1)设置标志位为 true

2)如果该线程正在阻塞中(比如在执行 sleep)此时就会把阻塞状态唤醒.通过抛出异常的方式让 sleep 立即结束.(线程在执行sleep(),join (),wait() 方法时,都会进入阻塞状态)

同时需要注意:
当 sleep 被唤醒的时候, sleep 会自动的把 Thread线程内置的标志位给清空(true -> false).这就导致下次循环,循环仍然可以继续执行了!!!

因此:如果需要结束循环,就得在 catch 中搞个 break.

此处再具体说明一下sleep() 的工作原理:(分3种情况)
1)如果 sleep 执行的时候看到这个标志位是 false --> sleep 正常进行休眠操作.

2)如果当前标志位为 true,sleep 无论是刚刚执行还是已经执行了一半,都会触发两件事 :1.立即抛异常2.清空标志位为 false.
3)如果设置 interrupt 的时候,恰好, sleep 刚醒~~ 这个时候赶巧了,执行到下一轮循环的条件,就直接结束了但是这种概率非常低,毕竟 sleep 的时间已经占据了整个循环体的 99.99999999% 的时间了.几乎可以视为不可能事件.

那可能就又会有人问了,为啥 sleep 要清空标志位呢???
目的就是为了让线程自身能够对于线程何时结束,有一个更明确的控制~
当前interrupt 方法,不是让线程立即结束,而是告诉他,你该结束了,至于他是否真的要结束,立即结束还是等会结束,都是代码来灵活控制的。
interrupt 只是通知,而不是"命令".
为什么这么说呢?原因就是,t线程是否结束,是取决于自身的,也就是说,根据catch里的逻辑实现,可以结束,也可以不结束.

 那肯定又会有人问了:那为啥java 这里 不强制设定成命令结束"的操作? 只要调用 interrupt 就立即结束?? 

主要是设定成这种,非常不友好的~

线程 t 何时结束,一定是 t 自己最清楚~~ 交给 t 自身来决定比较好~~

映射到生活:如果正在跟领导打电话,你的女票让你去帮她拿东西.那此时,肯定是工作优先嘛!!些竟拿东西不是太重要的事,可以先搁置一会!!

3)线程等待

线程之间是并发执行的,操作系统对于线程的调度是无序的.无法判定两个线程谁先执行结束, 谁后执行结束!!!

在某些业务场景下,需要明确规定线程的结束顺序.可以使用线程等待来实现 -->join 方法

public class ThreadDemo 
    public static void main(String[] args) throws InterruptedException 
        Runnable target = () -> 
            for (int i = 0; i < 10; i++) 
                try 
                    System.out.println(Thread.currentThread().getName() 
                                       + ": 我还在工作!");
                    Thread.sleep(1000);
                catch (InterruptedException e) 
                    e.printStackTrace();
               
           
            System.out.println(Thread.currentThread().getName() + ": 我结束了!");
       ;
        Thread thread1 = new Thread(target, "李四");
        Thread thread2 = new Thread(target, "王五");
        System.out.println("先让李四开始工作");
        thread1.start();
        thread1.join();
        System.out.println("李四工作结束了,让王五开始工作");
        thread2.start();
        thread2.join();
        System.out.println("王五工作结束了");
   

输出结果:

当前是在main线程里执行的thread1.join()(也就是说是让main线程等待thread1线程).

那么在main线程里执行的thread1.join0 的时候,此时有两种情况:

1)如果 thread线程仍在继续运行,那么main线程就会暂时不参与程调度,等待thread1 线程结束main线程才继续执行代码逻辑.
2)如果thread1线程已经结束,那么main线程也就不用等待了,就直接继续执行自己的代码逻辑了.

但不管是哪种情况,都能保证thread1线程是先于main线程结束的.

4)线程休眠

方法:public static void sleep(long millis) throws InterruptedException

说明:

1)单位是ms

2)sleep是Thread类里的静态方法,通过类名可以直接调用.

3)作用是休眠当前进程.

4)需要用try-catch处理InterruptedException中断异常(意思就是 sleep 睡眠过程中,还没到点就提前唤醒了).

5)获取线程实例

方法:public static Thread currentThread()

说明:在哪个线程里调用,返回的就是哪个线程的对象引用.

public class ThreadDemo 
    public static void main(String[] args) 
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName());
   

输出结果:main

多提一句:多线程编程在Java中很常见,也很重要.务必掌握!!!

今天不想敲代码,所以才去敲.

uu们加油呀!!

 

以上是关于JavaEE--Thread 类的基本用法(不看你会后悔的嘿嘿)的主要内容,如果未能解决你的问题,请参考以下文章

几个强大的工具类,不看你得后悔咯

分享在github超酷超炫特效动画,不看你会懊悔的。

2021秋招不看你就输了,Linux C/C++嵌入式笔试面试问题集合,

轻松上手NIO的框架技术Netty,Mina,不看你肯定后悔!!!

总结:那些热门的开源游戏服务器框架,还不看你就out了

Markdown的标准用法,不看会后此生