java高并发编程系列二:Thread API介绍

一.Thread API介绍

1.Thread.sleep()只会导致当前的线程进入指定毫秒时间的休眠。单位毫秒数,休眠有一个重要的特性就是不会丢失monitor锁的所有权。

例子:

其中:%d %f,是C语言中的格式输出符号,%d 输出整形值,%f 表示输出浮点型值。

TimeUnit枚举类也可以线程休眠,对、它sleep方法进行了很好的封装,使用TimeUnit可以省去换算单位时间,代码可读性更好。而且TimeUnit比sleep更强大。他们在不同的包。

2.yield方法属于一种启发式的方法,Thread.yield会提醒调度器当前线程可以放弃拥有的CPU资源(CPU执行权),如果CPU资源紧张,CPU资源执行转移切换到别的线程;如果资源不紧张,则会忽略这种提醒。Thread.yield可能会使当前线程进入RUNNABLE状态。一般不太常用。

输出结果 0,1,出现很多次

总结:

a sleep导致线程指定的时间,没有消耗CPU时间片。会在给定的时间内释放CPU资源。

b yield只是对CPU的一个提示,如果没有忽略提示,导致线程上下文的切换;当前线程RUNNING切换为RUNNABLE状态。

c sleep保证完成休眠,yield不一定。

d. 一个线程sleep,另一个线程interrupt会捕获到中断信号,yield不会。

3.设置线程的优先级

public final void setPriority(int newPriority)

获取优先级;

进程有进程的优先级,线程有线程的优先级,理论上优先级较高的线程会获取更多的被CPU调度的机会,设计优先级也是提示CPU的操作,并不会每次获得。

如果系统繁忙时,设置优先级可能会获得更多的优先级,但在闲时几乎不会有任何作用。

通过源码可以看出线程的优先级最小是1最大是10,越大优先级越高,如果指定的优先级大于所在group的优先级,将会失效,取而代之的是group的优先级。

线程的默认优先级是5,因为默认子线程和父线程一致,mian线程的优先级是5,由它派生出的也是5.

一般情况下不会对线程设置优先级,更不会让业务严重依赖线程的优先级别。这种方式是不可取的。一般定义线程使用默认优先级就好了。

4.获取线程ID,线程ID在整个JVM进程中都是唯一的,并且是从0开始递增的,在JVM进程启动的时候已经开创了很多个线程,所以我们自己创建的绝不会从0开始。

5.获取当前线程

6.设置线程上下文加载器

获取线程上下文的类加载器,就是说这个线程是由哪个类加载的,如果修改的话,默认保持与父线程同样的类加载器。

设置该线程的类加载器,可以修改java类加载器的父委托机制。

7.线程interrupt

a interrupt 线程中断方法,调用以下的方法都会使线程进入阻塞状态,而另一个线程调用被阻塞线程的interrupt方法,则会打断这种阻塞,因此也称为可中断方法,并不会结束线程的生命周期,只是结束阻塞状态。

一旦线程被中断,就会抛出一个InterruptedException异常,通知线程被打断了。

如果一个在非阻塞的线程被中断,其中flag标识将被设置,如果线程正在执行可中断方法(sleep)被阻塞时,反而会导致flag被清除。

如果线程死亡,interrupt会直接被忽略。

Object 的 wait

Object 的 wait()

Object 的 wait(long)

Object 的 wait(long,int)

Thread 的sleep(long)

Thread 的 sleep(long,int)

Thread 的 join

Thread 的 join (long)

Thread 的 join (long,int)

InterruptibleChannel的io操作

Selector 的 wakeup

b isInterrupted 判断当前线程是否被中断,该方法只是对中断标识的一个判断,不会影响其发生改变。

可中断方法例如sleep,捕获到中断异常时,,之后就会擦除 interrupt flag的标识。

可中断方法 sleep,捕获到中断信号异常,为了不影响线程中其他方法的执行,将线程的interrupt复位。

c interrupted 是一个静态方法,也是用于当前线程是否中断,它和isInterrupted 有很大区别,调用用这个方法会擦除线程的interrupt标识。

如果线程被中断了,第一次调用该方法会返回true,以后都是false。除非被再次中断。

8.线程join

join方法和sleep也是属于可中断方法,也就是说如果有其他线程调用了当前的interrupt方法,它也会捕获到中断信号,并且擦除线程interrupt 标识。

final void join()

final syncchronized void join(long,int) 毫秒数,纳秒数

final syncchronized void join(long)

有A,B2个线程,B线程调用A线程的join阻塞方法,会使B线程进入等待状态,B线程处于BLOCKED状态的。直到A线程执行完毕,生命周期结束,或者join到了给定的时间,B线程才会继续执行。

也就是说调用了A线程join,A线程开始执行,B线程进入等待。直到结束或者join时间达到。

以上例子输出结果,线程1和线程2交替输出之后main线程才运行输出。注意:线程1join,开始运行,mian等待,线程2已经start了,所以也输出。

join当时会使当前线程永远的等待下去,直到期间被另外线程中断,或者join线程执行结束。还可以指定毫秒数,在给定时间到达了之后,当前线程也会退出阻塞。如果一个线程已经结束了生命周期,因为线程不能重复激活,所以调用其join方法没有任何作用,也没有报出异常。

运行例子:多线程查询各大公司航班信息,统一返回整理结果

9关闭线程的方式

a.jdk有一个deprecated 不赞成方法stop,官方已经不推荐使用了,其存在一个问题,它在关闭线程时,可能不会释放掉monitor的锁,不建议使用,后面的版本可能会移除。

b.正常关闭,

线程占用资源较少,耗时比较短,时间可控的话,就是等线程运行结束,生命周期结束,正产退出即可。

c.捕获中断信号关闭线程,我们通过new Thread创建的线程,其派生的成本比较高的,往往在其中做一些事,比如监听,心跳检查等,系统需要退出的时候可以使用中断线程的方式去退出。

以上根据线程的中断标识去决定退出线程的,如果在线程中执行可中断方法,也可以使用捕获中断信号来进行退出线程。

d.使用volatile开关标识控制关闭

e.异常退出

在一个线程执行单元不允许抛出checked异常的,如果需要抛出checked异常,可以把它封装成unchecked异常(RuntimeExecption),进而结束线程的生命周期的。

10.进程假死以及jvisualvm工具的使用

进程假死就是进程依然存在,但是没有日志输出,程序不进行任何的作业,就像死掉一样,事实上并没有死,绝大部分原因是因为线程阻塞,或者线程出现了死锁的情况,比如2个线程同时操作一个业务,分别获得一把不同锁,谁也没有释放锁,线程一直等在那里死锁了,获取锁首先获取较大的资源的锁,再去获取小的锁,释放锁则相反。

这时可以借助一些工具判断,jconsole,jvisualvm。

jvisualvm:jdk更新的,能够监控线程,内存情况,查看方法的CPU时间和内存中的对 象,已被GC的对象,反向查看分配的堆栈(如100个String对象分别由哪几个对象分配出来的).

VisualVM使用简单,几乎0配置,功能还是比较丰富的,几乎囊括了其它JDK自带命令的所有功能。

内存信息

线程信息

Dump堆(本地进程)

Dump线程(本地进程)

打开堆Dump。堆Dump可以用jmap(jmap -J-d64 -heap pid)来生成。

打开线程Dump

生成应用快照(包含内存信息、线程信息等等)

性能分析。 :idea: CPU分析(各个方法调用时间,检查哪些方法耗时多),内存分析(各类对象占用的内存,检查哪些类占用内存多)

出现假死的情况,可以打开jvisualvm查看哪些活跃线程,线程状态,调用的方法。

总结:获取线程的API,getid,getName,getPriority,currentThread;阻塞的方法:sleep,join等。

@$问题:主线程结束了线程的写法,以前的new Thread需要写run方法,lamabda表达式为什么没有重写run方法?

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181203G16MGY00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券