Java中的几种线程创建方式

首先在操作系统层面,宏观角度上来讲,线程的生命周期是五态

 

1. 初始状态 指的是线程已经被创建,但是还不允许分配 CPU 执行。这个状态属于编程
语言特有的,不过这里所谓的被创建,仅仅是在编程语言层面被创建,而在操作系统层
面,真正的线程还没有创建。
2. 可运行状态 ,指的是线程可以分配 CPU 执行。在这种状态下,真正的操作系统线程已
经被成功创建了,所以可以分配 CPU 执行。
3. 当有空闲的 CPU 时,操作系统会将其分配给一个处于可运行状态的线程,被分配到
CPU 的线程的状态就转换成了 运行状态
4. 运行状态的线程如果调用一个阻塞的 API(例如以阻塞方式读文件)或者等待某个事件
(例如条件变量),那么线程的状态就会转换到 休眠状态 ,同时释放 CPU 使用权,休眠
状态的线程永远没有机会获得 CPU 使用权。当等待的事件出现了,线程就会从休眠状态
转换到可运行状态。
5. 线程执行完或者出现异常就会进入 终止状态 ,终止状态的线程不会切换到其他任何状
态,进入终止状态也就意味着线程的生命周期结束了。
然而在JAVA层面 是六种
 
1. NEW(初始化状态)
2. RUNNABLE(可运行状态+运行状态)
3. BLOCKED(阻塞状态)
4. WAITING(无时限等待)
5. TIMED_WAITING(有时限等待)
6. TERMINATED(终止状态)

 

方式1:使用 Thread类或继承Thread类
1 // 创建线程对象
2 Thread t = new Thread () {
3 public void run () {
4 // 要执行的任务
5 }
6 };
7 // 启动线程
8 t . start ();
 
方式2:实现 Runnable 接口配合Thread
把【线程】和【任务】(要执行的代码)分开
Thread 代表线程
Runnable 可运行的任务(线程要执行的代码)
1 Runnable runnable = new Runnable () { 2 public void run (){
3 // 要执行的任务
4 }
5 };
6 // 创建线程对象
7 Thread t = new Thread ( runnable );
8 // 启动线程
9 t . start ();
方式3:使用有返回值的 Callable
1 class CallableTask implements Callable < Integer > {
2 @Override
3 public Integer call () throws Exception {
4 return new Random (). nextInt ();
5 }
6 }
7 // 创建线程池
8 ExecutorService service = Executors . newFixedThreadPool ( 10 );
9 // 提交任务,并用 Future 提交返回结果
10 Future < Integer > future = service . submit ( new CallableTask ());
方式4:使用 lambda
1 new Thread (()
> System . out . println ( Thread . currentThread (). getName ())). start ();

Runnable和Callable的区别

Callable有返回值 可以捕获异常,依据于线程池

创建线程的方式 分为4这种 但其实底层都是一种创建方式

 

 

实际上还是调用了Runnable的run方法

也就是说第一种第二种和第四种是一样的方式创建线程

线程池的底层也是使用Thread来创建线程

所以java提供的几种方式 可以当做一个工具类 开发人员可以选择不同的方式来调用

Version:0.9 StartHTML:0000000105 EndHTML:0000018120 StartFragment:0000000141 EndFragment:0000018080

Thread常用方法
sleep方法
调用 sleep 会让当前线程从 Running 进入TIMED_WAITING状态, 不会释放对象锁
其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出
InterruptedException,并且会清除中断标志
睡眠结束后的线程未必会立刻得到执行
sleep当传入参数为0时,和yield相同
yield方法
yield会释放CPU资源,让当前线程从 Running 进入 Runnable状态,让优先级更高
(至少是相同)的线程获得执行机会, 不会释放对象锁
假设当前进程只有main线程,当调用yield之后,main线程会继续运行,因为没有比
它优先级更高的线程;
具体的实现依赖于操作系统的任务调度器
join方法
等待调用join方法的线程结束之后,程序再继续执行,一般用于等待异步线程执行完结果之
后才能继续运行的场景。
1 public class ThreadJoinDemo {
2
3 public static void main ( String [] sure ) throws InterruptedException {
4
5 Thread t = new Thread ( new Runnable () {
6 @Override
7 public void run () {
8 System . out . println ( "t begin" );
9 try {
10 Thread . sleep ( 5000 );
11 } catch ( InterruptedException e ) {
12 e . printStackTrace ();
13 }
14 System . out . println ( "t finished" );
15 }
16 });
17 long start = System . currentTimeMillis ();
18 t . start ();
19 // 主线程等待线程 t 执行完成
20 t . join ();
21
22 System . out . println ( " 执行时间 :" + ( System . currentTimeMillis () start ));
23 System . out . println ( "Main finished" );
24 }
25 }
 
如何正确优雅的停止线程?
stop方法
stop()方法已经被jdk废弃,原因就是stop()方法太过于暴力,强行把执行到一半的线程终止。
1 public class ThreadStopDemo {
2
3 private static Object lock = new Object ();
4
5 public static void main ( String [] args ) throws InterruptedException {
6
7 Thread thread = new Thread ( new Runnable () {
8 @Override
9 public void run () {
10 synchronized ( lock ) { 11 System . out . println ( Thread . currentThread (). getName () + " 获取锁 " );
12 try {
13 Thread . sleep ( 60000 );
14 } catch ( InterruptedException e ) {
15 e . printStackTrace ();
16 }
17 }
18 System . out . println ( Thread . currentThread (). getName () + " 执行完成 " );
19 }
20 });
21 thread . start ();
22 Thread . sleep ( 2000 );
23 // 停止 thread ,并释放锁
24 thread . stop ();
25
26 new Thread ( new Runnable () {
27 @Override
28 public void run () {
29 System . out . println ( Thread . currentThread (). getName () + " 等待获取锁 " );
30 synchronized ( lock ) {
31 System . out . println ( Thread . currentThread (). getName () + " 获取锁 " );
32 }
33 }
34 }). start ();
35
36 }
37 }
stop会释放对象锁,可能会造成数据不一致。
Java线程的中断机制
Java没有提供一种安全、直接的方法来停止某个线程,而是提供了中断机制。 中断机制是一
种协作机制,也就是说通过中断并不能直接终止另一个线程,而需要被中断的线程自己处理。
中断的线程拥有完全的自主权,它既可以选择立即停止,也可以选择一段时间后停止,也可以选
择压根不停止。
API的使用
interrupt(): 将线程的中断标志位设置为true,不会停止线程
isInterrupted(): 判断当前线程的中断标志位是否为true,不会清除中断标志位
Thread.interrupted():判断当前线程的中断标志位是否为true,并清除中断标志
位,重置为fasle 1 public class ThreadInterruptTest {
2
3 static int i = 0 ;
4
5 public static void main ( String [] args ) {
6 System . out . println ( "begin" );
7 Thread t1 = new Thread ( new Runnable () {
8 @Override
9 public void run () {
10 while ( true ) {
11 i ++ ;
12 System . out . println ( i );
13 //Thread.interrupted() 清除中断标志位
14 //Thread.currentThread().isInterrupted() 不会清除中断标志位
15 if ( Thread . currentThread (). isInterrupted () ) {
16 System . out . println ( "=========" );
17 }
18 if ( i == 10 ){
19 break ;
20 }
21
22 }
23 }
24 });
25
26 t1 . start ();
27 // 不会停止线程 t1, 只会设置一个中断标志位 flag=true
28 t1 . interrupt ();
29 }
30 }
利用中断机制优雅的停止线程
1 while ( ! Thread . currentThread (). isInterrupted () && more work to do ) {
2 do more work
3 }
1 public class StopThread implements Runnable {
2
3 @Override
4 public void run () {
5 int count = 0 ;
6 while ( ! Thread . currentThread (). isInterrupted () && count < 1000 ) {
7 System . out . println ( "count = " + count ++ ); 8 }
9 System . out . println ( " 线程停止: stop thread" );
10 }
11
12 public static void main ( String [] args ) throws InterruptedException {
13 Thread thread = new Thread ( new StopThread ());
14 thread . start ();
15 Thread . sleep ( 5 );
16 thread . interrupt ();
17 }
18 }
注意:使用中断机制时一定要注意是否存在中断标志位被清除的情况
sleep 期间能否感受到中断
修改上面的代码,线程执行任务期间有休眠需求
1 @Override
2 public void run () {
3 int count = 0 ;
4 while ( ! Thread . currentThread (). isInterrupted () && count < 1000 ) {
5 System . out . println ( "count = " + count ++ );
6
7 try {
8 Thread . sleep ( 1 );
9 } catch ( InterruptedException e ) {
10 e . printStackTrace ();
11 }
12 }
13 System . out . println ( " 线程停止: stop thread" );
14 }
处于休眠中的线程被中断, 线程是可以感受到中断信号的,并且会抛出一个
InterruptedException 异常,同时清除中断信号,将中断标记位设置成 false 。这样就会导致
while条件Thread.currentThread().isInterrupted()为false,程序会在不满足count < 1000这个
条件时退出。如果不在catch中重新手动添加中断信号,不做任何处理,就会屏蔽中断请求,有
可能导致线程无法正确停止。
1 try {
2 Thread . sleep ( 1 );
3 } catch ( InterruptedException e ) { 4 e . printStackTrace ();
5 // 重新设置线程中断状态为 true
6 Thread . currentThread (). interrupt ();
7 }
sleep可以被中断 抛出中断异常:sleep interrupted, 清除中断标志位
wait可以被中断 抛出中断异常:InterruptedException, 清除中断标志位
 
Java线程间通信
volatile
volatile有两大特性,一是可见性,二是有序性,禁止指令重排序,其中可见性就是可以让线程
之间进行通信。
1 public class VolatileDemo {
2
3 private static volatile boolean flag = true ;
4
5 public static void main ( String [] args ) {
6
7 new Thread ( new Runnable () {
8 @Override
9 public void run () {
10 while ( true ){
11 if ( flag ){
12 System . out . println ( "trun on" );
13 flag = false ;
14 }
15 }
16 }
17 }). start ();
18
19 new Thread ( new Runnable () {
20 @Override
21 public void run () {
22 while ( true ){
23 if ( ! flag ){
24 System . out . println ( "trun off" );
25 flag = true ;
26 }
27 }
28 }
29 }). start (); 30 }
31 }
等待唤醒(等待通知)机制
等待唤醒机制可以基于wait和notify方法来实现,在一个线程内调用该线程锁对象的wait方法,
线程将进入等待队列进行等待直到被唤醒。
1 public class WaitDemo {
2 private static Object lock = new Object ();
3 private static boolean flag = true ;
4
5 public static void main ( String [] args ) {
6 new Thread ( new Runnable () {
7 @Override
8 public void run () {
9 synchronized ( lock ){
10 while ( flag ){
11 try {
12 System . out . println ( "wait start ......." );
13 lock . wait ();
14 } catch ( InterruptedException e ) {
15 e . printStackTrace ();
16 }
17 }
18
19 System . out . println ( "wait end ....... " );
20 }
21 }
22 }). start ();
23
24 new Thread ( new Runnable () {
25 @Override
26 public void run () {
27 if ( flag ){
28 synchronized ( lock ){
29 if ( flag ){
30 lock . notify ();
31 System . out . println ( "notify ......." );
32 flag = false ;
33 }
34
35 }
36 } 37 }
38 }). start ();
39 }
40 }
LockSupport是JDK中用来实现线程阻塞和唤醒的工具,线程调用park则等待“许可”,调用
unpark则为指定线程提供“许可”。 使用它可以在任何场合使线程阻塞,可以指定任何线程进行
唤醒,并且不用担心阻塞和唤醒操作的顺序,但要注意连续多次唤醒的效果和一次唤醒是一样
的。
1 public class LockSupportTest {
2
3 public static void main ( String [] args ) {
4 Thread parkThread = new Thread ( new ParkThread ());
5 parkThread . start ();
6
7 System . out . println ( " 唤醒 parkThread" );
8 LockSupport . unpark ( parkThread );
9 }
10
11 static class ParkThread implements Runnable {
12
13 @Override
14 public void run () {
15 System . out . println ( "ParkThread 开始执行 " );
16 LockSupport . park ();
17 System . out . println ( "ParkThread 执行完成 " );
18 }
19 }
20 }
管道输入输出流
管道输入/输出流和普通的文件输入/输出流或者网络输入/输出流不同之处在于,它主要用于线程
之间的数据传输,而传输的媒介为内存。管道输入/输出流主要包括了如下4种具体实现:
PipedOutputStream、PipedInputStream、PipedReader和PipedWriter,前两种面向字节,
而后两种面向字符。
1 public class Piped {
2 public static void main ( String [] args ) throws Exception {
3 PipedWriter out = new PipedWriter ();
4 PipedReader in = new PipedReader ();
5 // 将输出流和输入流进行连接,否则在使用时会抛出 IOException 6 out . connect ( in );
7
8 Thread printThread = new Thread ( new Print ( in ), "PrintThread" );
9
10 printThread . start ();
11 int receive = 0 ;
12 try {
13 while (( receive = System . in . read ()) != ‐ 1 ) {
14 out . write ( receive );
15 }
16 } finally {
17 out . close ();
18 }
19 }
20
21 static class Print implements Runnable {
22 private PipedReader in ;
23
24 public Print ( PipedReader in ) {
25 this . in = in ;
26 }
27
28 @Override
29 public void run () {
30 int receive = 0 ;
31 try {
32 while (( receive = in . read ()) != ‐ 1 ) {
33 System . out . print (( char ) receive );
34 }
35 } catch ( IOException ex ) {
36 }
37 }
38 }
39 }
Thread.join
join可以理解成是线程合并,当在一个线程调用另一个线程的join方法时,当前线程阻塞等
待被调用join方法的线程执行完毕才能继续执行,所以join的好处能够保证线程的执行顺序,但
是如果调用线程的join方法其实已经失去了并行的意义,虽然存在多个线程,但是本质上还是串
行的,最后join的实现其实是基于等待通知机制的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值