技术文章

高并发必备篇(四):线程的状态、调度和操作方法(下)

上期的干货分享,我们给大家介绍了Java中线程的几个状态以及线程的调度,这一期我们接着来讲高并发必备篇之线程的操作方法。

 

(1) 线程的优先级-Priority

线程同样也有优先级设定,线程的优先级从1到10,数字越大则优先级越高,如果我们创建线程而没有设置优先级的话,那么优先级就是默认的5。

 

值得注意的是并不是优先级高的线程就一定会比优先级低的线程先执行,线程的优先级越高表示的是获取到的CPU执行时间片越多,跟优先级低的线程比抢夺到CPU执行时间的几率就越高。

 

我们可以通过Thread的setPriority()方法来设置线程的优先级,同样也可以通过getPriority();方法获取线程的优先级。案例如下:

高并发必备篇(四):线程的状态、调度和操作方法(下)

 

行结果:

高并发必备篇(四):线程的状态、调度和操作方法(下)

 

运行多次程序结果分析我们会发现线程2获取到的机会更大一些。

 

(2) 线程的礼让-yeild

Thread.yeild() 是Thread类的静态方法可以直接调用表示线程的礼让,线程的礼让指的是当前线程暂时放弃CPU的执行时间礼让一下给其他线程,而礼让的同时自身进入就绪状态。

 

因为礼让之后自身也进入就绪状态,所以yeild礼让之后自身还是有几率抢夺到CPU的执行时间,只不过一旦礼让之后优先级越高的线程抢夺到的几率越高。

 

举个例子:好比排队买菜,某一天轮到张三买菜了,但是张三礼让了一下说“大家公平竞争,谁今天先到卖菜的地方谁就先买”而张三这么一礼让明天买菜的人就看谁先到了,有可能买菜的是他也有可能是别人。

 

案例如下:

高并发必备篇(四):线程的状态、调度和操作方法(下)

 

运行结果:

高并发必备篇(四):线程的状态、调度和操作方法(下)

 

我们发现并不是每次都能礼让成功的。虽然yeild方法可以做到礼让,但是其实在开发中用的场合很少。更多是用于调式和测试的作用,源代码说明如下:

高并发必备篇(四):线程的状态、调度和操作方法(下)

 

(3) 线程的插队-join

 Java线程面试的时候很多人遇到过这样的问题,“Java中如何让多个线程按照自己指定的顺序执行?”没错,这个问题最简单的实现就是使用Thread的join() 方法来实现了。

 

thread.join()的意思表示当前线程会从运行状态变为阻塞状态,需要等待插入的线程执行完终止之后,才会从thread.join的阻塞状态变为可运行状态。案例如下:

高并发必备篇(四):线程的状态、调度和操作方法(下)

 

运行下代码我们就会发现,无论运行多少次,一直就是线程t1的代码先执行完毕然后才执行到t2的代码。

 

当然join() 方法并不是用来保证线程的顺序性的。查看下join的源代码我们会发现其实底层还是使用的Object类的wait()方法,如图:

高并发必备篇(四):线程的状态、调度和操作方法(下)

 

join方法中当通过isAlive() 方法判断当前线程是运行状态的时候,就进行阻塞主线程让出cpu的资源给t1线程,而t1可以一直抢夺cpu资源到执行完成是因为join方法被synchronized 修饰了。

 

之前介绍过synchronized关键字的一些意思,这里我们先简单说明下被synchronized 修饰的方法调用线程就会获取到同步锁,获取同步锁的线程可以一直执行完毕才会释放锁给其他线程执行。

 

join()方法主要用于一些多线程协调完成一个任务的执行,或者可以顺序执行的场景。

 

(4) 线程的中断

之前我们在使用Thread.sleep()和Thread.join()的时候发现都需要抛出异常InterruptedException即线程被中断异常。为什么要抛出这个异常?

 

在Java中,一个线程是不能终止另一个线程的,除非线程自己程序想退出或者程序结束了。以前的时候Thread类提供了stop()、destroy()等方法可以强制结束一个线程,但是现在这些方法都没有得到保留下来。

 

那如何结束一个线程呢?其实每个线程都拥有一个flag,标志着线程的中断标识。如果一个线程A想让线程B退出,则A将B的中断标示(interrupt flag)置为true,我们说“线程A向线程B发了中断信号”。

 

此时如果B检查到了中断标识为true,说明有线程想让它中断,线程B通过自己判断是否需要自愿退出(也可以不退出,不能强制)。

 

而在执行一些耗时操作的时候,例如sleep()、join()、wait()等,需要经常check interrupt的状态,并且一旦发现为true,就会立刻抛出InterruptedException 告诉你其他线程向你发送了中断信号。

 

Java中通过Thread的interrupt(true) 来设置一个线程的中断信号为true,interrupted()和isInterrupted()方法可以获取线程的中断标识是否为true,不同的是interrupted() 在获取的标识后会清除标识即把标识改为false。我们看下面结束线程的案例:

高并发必备篇(四):线程的状态、调度和操作方法(下)

 

运行代码,当主线程调用 t.interrupt();的时候子线程t执行结束。

 

(5) 守护线程(后台线程)

守护线程又叫“服务线程”,它是后台线程,守护线程最显著的特点就是JVM中如果没有用户创建的前台线程的时候就会自动退出,所以守护线程一般都是给程序中其他对象和线程提供一些公共服务或者进行一些后台任务执行。

守护线程的优先级都很低,典型的守护线程就是GC(垃圾回收器),都知道GC主要负责我们JVM堆和方法区中内存的回收,如果JVM中除了GC线程外其他的都运行完没有了,那么GC也就不需要回收垃圾所以作为守护线程就自动退出了。

Java中通过 setDaemon(true)来设置线程为“守护线程”,案例代码如下:

高并发必备篇(四):线程的状态、调度和操作方法(下)

 

运行之后我们会发现当i=99之后,上面的线程运行完毕,然后后台线程也会退出程序。

 

以上就是我们线程中的一些基本的操作方法使用和讲解了,通过这些方法的运用希望大家对于线程的了解和使用更加的详细,下一篇中我们开始讲解如何保证线程并发安全的问题。