Java多线程二: Thread中几个比较重要的方法

2022-04-24 15:29:23 浏览数 (1)

Thread中有一些常见的方法,对于我们学习多线程来说,这些方法都需要了解,包括像sleep,join,yield等,学好了这些方法以及原理,在后续的学习中肯定会事半功倍。对于像interrupt这类方法,线程中断的方法,会在下一期文章中详细介绍,线程中断涉及的方法比较多,也容易混淆,但是很重要,所以这里我们就简单提一下这个方法就好。

1. sleep()

sleep作为最常见的方法之一,其作用就是使调用sleep的所在线程进入睡眠状态。它会让调用该方法的所在线程主动放弃CPU资源,进入阻塞状态,如果指定了睡眠时间,到达了指定时间之后线程就会进入就绪状态,等待调度器的调用。sleep()方法是Thread类的静态方法,如果调用线程对象.sleep()方法并不是该线程就休眠,而是哪一个线程里面执行了sleep()方法哪一个线程就休眠。

2. join()

执行该方法的线程进入阻塞状态,直到调用该方法的线程结束后再由阻塞转为就绪状态。

使用场景:主线程中开启了一个几个子线程进行数据计算,但是主线程最后会需要其中某一个线程的计算结果,这时候就需要主线程调用子线程的join方法,等待子线程完成数据计算任务,然后再进行后续的操作。

代码语言:javascript复制
 public static void main(String[] args) throws InterruptedException {
     Thread thread1 = new Thread(()->{
         try {
             TimeUnit.SECONDS.sleep(2);
             System.out.println("子线程1执行完了");
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
     });
     Thread thread2 = new Thread(()->{
         try {
             TimeUnit.SECONDS.sleep(5);
             System.out.println("子线程2执行完了");
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
 
     });
     thread2.start();
     thread1.start();
     try {
         thread1.join();
     } catch (InterruptedException e) {
         e.printStackTrace();
     }
     System.out.println("主线程执行完了");
 }

子线程2睡眠5秒,子线程1睡眠2秒,正常情况下应该是主线程最先完成,然后才是子线程1,子线程2执行完成,但是在代码中调用了thread1.join()方法,所以主线程需要等待子线程1完成之后再继续执行,而不需要等待子线程2执行完成。

执行结果:

3. yield()

这个方法只是提出申请释放CPU资源,至于能否成功释放由JVM决定,也就是说该线程会回到就绪状态(RUNABLE),随时可能被CPU调度到。

简单来说就是提示CPU,我这个线程主要的内容执行完了,CPU可以将时间片拿去给其他线程使用,但只是给出一个建议信息,建议CPU去调度同级线程中优先级更高的线程。

4. setPriority()

设置线程的优先级,默认为5,线程优先级在1-10之间,有set方法一般就会有get方法,默认情况下,getPriority()就会返回默认的线程优先级5,源码如下:

代码语言:javascript复制
 /**
   * The minimum priority that a thread can have.
   */
 public final static int MIN_PRIORITY = 1;
 
 /**
   * The default priority that is assigned to a thread.
   */
 public final static int NORM_PRIORITY = 5;
 
 /**
   * The maximum priority that a thread can have.
   */
 public final static int MAX_PRIORITY = 10;

如果设置线程优先级大于10或者是小于1,会抛出IllegalArgumentException,这点也在setPriority()中可以找到。

代码语言:javascript复制
if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
     throw new IllegalArgumentException();
 }

虽然设置了线程优先级,但是在线程数没有达到电脑最大线程数的时候,线程优先级高的不一定先执行完成。

代码语言:javascript复制
 Thread thread1 = new Thread(()->{
     System.out.println("子线程1执行完了");
 });
 Thread thread2 = new Thread(()->{
     System.out.println("子线程2执行完了");
 });
 thread2.setPriority(6);
 thread2.start();
 thread1.start();

大多数情况下都是线程2先执行完,但是也有极少数情况下,会是线程1先执行完。

5. setName()

设置线程名称,两种方式可以设置,一是在创建线程的时候通过构造方法传入,第二种是通过setName()方法传入。当然不传的时候,也会有线程的默认的名字。

代码语言:javascript复制
 Thread thread1 = new Thread(()->{
     System.out.println("子线程1执行完了");
 });
 Thread thread2 = new Thread(()->{
     System.out.println("子线程2执行完了");
 });
 Thread thread3 = new Thread(()->{
     System.out.println("子线程3执行完了");
 },"线程3");
 thread2.setName("线程2");
 thread2.start();
 thread1.start();
 thread3.start();
 System.out.println("线程1的名称:" thread1.getName());//Thread-0
 System.out.println("线程2的名称:" thread2.getName());//线程2
 System.out.println("线程2的名称:" thread3.getName());//线程3

6. currentThread()

获取当前线程,得到一个Thread类。

代码语言:javascript复制
 Thread thread1 = new Thread(()->{
     String name = Thread.currentThread().getName();
     System.out.println(name);
 });
 thread1.start();
 String name = Thread.currentThread().getName();
 System.out.println(name);

7. setDaemon()

设置该线程为守护线程,参数为布尔值。所谓守护线程就是为用户线程服务的线程,比如说垃圾回收的线程等等,当用户线程结束,守护线程完成任务之后自动结束,我们正常创建的线程即为用户线程,使用Thread.setDaemon(true)方法, 线程变成守护线程 。如果用户希望主线程结束之后,其他线程自动结束,则可以将其他线程设置为守护线程。

代码语言:javascript复制
public final void setDaemon(boolean on) {
     checkAccess();
     if (isAlive()) {
         throw new IllegalThreadStateException();
     }
     daemon = on;
 }

isAlive()方法判断线程是否存活,那么setDaemon()中调用这个方法的意思就是,如果线程存活的话,就会抛出不合法的线程状态异常。所以就限制了,调用setDaemon()方法必须在调用start()方法之前,否则就会抛出异常。

8. interrupt()

利用这个方法可以中断线程,值得注意的是,如果该线程处于等待,阻塞,或者无限期等待状态,那么就会抛出InterruptedException异常,从而提前结束线程。但是不能中断I/O阻塞和synchronized锁阻塞。

先看看等待,阻塞的情况;

sleep方法

代码语言:javascript复制
 Thread thread = new Thread(()->{
     try {
         Thread.sleep(10000);
     } catch (InterruptedException e) {
         e.printStackTrace();
     }
 });
 thread.start();
 thread.interrupt();
 
 会抛出异常 java.lang.InterruptedException: sleep interrupted

wait方法

代码语言:javascript复制
 public class InterruptTest {
     private static String str = "test";
     public static void main(String[] args) {
         Thread thread = new Thread(()->{
             synchronized (str){
                 try {
                     str.wait();
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
             }
         });
         thread.start();
         thread.interrupt();
     }
 }
 抛出异常:java.lang.InterruptedException

join方法

代码语言:javascript复制
 Thread thread1 = new Thread(()->{
     while (true){
 
     }
 });
 Thread thread = new Thread(()->{
     try {
         thread1.join();
     } catch (InterruptedException e) {
         e.printStackTrace();
     }
 });
 thread1.start();
 thread.start();
 thread.interrupt();
 
 抛出异常:java.lang.InterruptedException

synchronized锁阻塞测试:

代码语言:javascript复制
 public class InterruptTest {
     private static String str = "test";
     public static void main(String[] args) {
         Thread thread = new Thread(() -> {
             synchronized (str) {
                 while (true){
                 
                 }
             }
         });
         thread.start();
         thread.interrupt();
     }
 }

这段代码会一直执行下去,不会被中断,也不会抛出异常。关于线程中断的东西后面再写一篇文章,点个关注不迷路。

0 人点赞