Thread类的基本用法
- 一.线程的创建
- 1.定义类来继承Thread,重写run()方法
- 2.实现 Runnable接口, 重写 run()
- 3.继承 Thread, 重写 run,使用匿名内部类
- 4.实现 Runnable接口, 重写 run, 使用匿名内部类
- 5.使用lambda表达式
- 二.线程中断
- 三.线程等待
- 四.线程的状态
- 五.获取线程的实例
- 总结
一.线程的创建
Thread 是 JAVA 标准库中描述一个线程的类,
我们常用的创建线程的方法有下面几种~
1.定义类来继承Thread,重写run()方法
run方法就表示线程要执行的具体任务(代码)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19class MyThread extends Thread{ //重写run()方法 @Override public void run() { while(true) { System.out.println("hello"); } } } public class ThreadDemo { public static void main(String[] args) { Thread t = new MyThread(); //start()方法就会在操作系统中真的创建出一个线程来,并运行run方法中的具体任务 t.start(); //直接调用run()也可以实现代码的运行,但是这种调用方式并没有创建线程 //t.run(); }
2.实现 Runnable接口, 重写 run()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class MyRunnable implements Runnable{ @Override public void run() { System.out.println("hello"); } } public class ThreaDemo3 { public static void main(String[] args) { //本质上和继承Thread重写run效果一样 Thread t = new Thread(new MyRunnable()); t.start(); } }
3.继承 Thread, 重写 run,使用匿名内部类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public class ThreadDemo4 { public static void main(String[] args) { //匿名内部类 //相当于创建了匿名的类,这个类继承了Thread Thread t = new Thread() { @Override public void run() { while(true) { System.out.println("hello"); try { //这里是让循环每1000ms跑一次,使用Thread.sleep()需要抛出异常,否则会报错 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }; t.start(); } }
4.实现 Runnable接口, 重写 run, 使用匿名内部类
1
2
3
4
5
6
7
8
9
10
11public static void main1(String[] args) { //实现Runnable 使用匿名内部类 Thread t = new Thread(new Runnable() { @Override public void run() { System.out.println("heall"); } }); t.start(); }
5.使用lambda表达式
使用lambda也可以达到同样的效果~~
1
2
3
4
5
6public static void main(String[] args) { Thread t = new Thread(() ->{ System.out.println("hello"); }); }
以上的这些创建线程的方式,本质都是一样的,都是借助Thread类,在内核上创建新的PCB,加入到内核的双向链表中…
只不过区别是要执行任务的方式不一样~~
如果想要清楚地看到线程的产生,可以在JDK内置的jconsole工具中查看到线程的信息~~~~
二.线程中断
在实际开发中,我们大多是情况都不会希望线程的run是一个死循环,更希望我们能够控制这个线程,按照需求来随时结束线程(线程中断)
具体方法:
1.使用boolean变量作为循环结束标志
2.使用标准库里的内置的标志:
●获取线程内置的标志位:isInterrupted()方法
●修改线程内置的标志位:Thread.interrupt()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28public static void main(String[] args) throws InterruptedException { Thread t = new Thread(){ @Override public void run() { //默认标志位为false //Thread.currentThread()能够获取当前线程的引用 while(!Thread.currentThread().isInterrupted()) { System.out.println("线程运行中...."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); //加个break来保证循环结束 break; } } } }; t.start(); //在主线程中,通过t.interrupt()来设置标记位 Thread.sleep(1000); //interrupt方法可能有两种行为: //1.如果当前线程正在运行,此时就会修改标记位为true //2.如果当前线程正在sleep/wait/等待锁...此时就会触发InterruptedException来进行唤醒 t.interrupt(); }
上述代码中的isInterrupted()是Thread的实例方法,
interrupted()这个方法也可以实现这样的目的,它是Thread类的一个静态(static)方法.(调用方法跟上面类似 , 这里就不代码演示了)~~
二者的区别:
使用静态的方法会自动清除标记位,按照上述流程调用interrupt()后标志位改为true , 但是之后会把标记位再次恢复为false;
而非静态的这个方法则 在之后不会对标记位再次进行修改~(可以根据实际的情况来判断需要使用这两个方法中的哪一种)
三.线程等待
线程与线程之间 , 调度顺序是完全不同的(这取决于操作系统调制器自身的实现),
那我们如果希望这个顺序是可控的 , 线程等待就是一种办法,来控制线程结束的先后顺序~~~
一种常见的逻辑 : t1线程,创建t2,t3,让这些新的线程来分别执行一些任务,最后让t1进行汇总任务,我们就需要t1线程最后结束,
t1.join();
这个代码就可以实现这个目的,调用这个线程时候就会阻塞等待(操作系统短时间内是不会让这个线程调度到CPU上的~) ~
我们来举个"栗子":
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(){ @Override public void run() { int count = 0; while(count < 5) { count++; System.out.println("线程运行中"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("运行结束"); } }; t1.start(); System.out.println("join开始执行"); t1.join();//阻塞等待 System.out.println("join结束"); } //运行后的结果 join开始执行 线程运行中 线程运行中 线程运行中 线程运行中 线程运行中 运行结束 join结束
根据上述代码以及运行后的结果 , 可以看到 , 执行start方法,就会立刻创建一个新的线程,同时main这个线程往下执行,执行到join方法,只要t1在运行中,join方法就会一直阻塞等待,一直到t1线程执行结束(run执行完了),才会继续往下执行~~
● join()方法还有两个版本
一个是无参数的版本,相当于是"死等",什么时候对应的线程运行完了,才会继续往下执行,不然就一直等待~
另一个是有参数的版本,其中的参数就是最大等待时间~~
四.线程的状态
用于辅助系统对于线程进行调度这样的属性~
通过getState()就可以查询到当前线程对应的状态:
NEW : Thread 对象创建出来了,但内核的PCB还没创建出来.
RUNNABLE : 当前PCB也创建出来了,同时这个PCB随时待命.(就绪) 这个线程可能是正在CPU上运行,也可能实在就绪队列中排队…
TIMED_WAITING : 表示当前的pcb在阻塞队列中等待呢~(这个等待是一个"带有结束时间"的等待 : Thread.sleep())
TERMINATED: 表示当前PCB已经结束了.Thread对象还在.此时调用获取状态,得到的就是这个状态
WAITING : 线程中如果调用了wait方法,也会阻塞等待,(死等),除非其他线程唤醒了该线程.
BLOCKED : 线程中尝试进行加锁,结果发现锁已经被其他线程占用了,也会阻塞等待,等其他线程放锁后,被唤醒~~
● 当前以上的几个状态都是java的Thread类状态,和操作系统内部PCB里面的状态的值并不是完全一样的~~
五.获取线程的实例
这里我在上文第二章的 线程中断 中代码有提到过,
通过Thread.currentThread()来获取当前线程的实例,也可以通过this来获取~~
1
2
3
4
5
6
7
8
9
10
11
12
13ublic static void main(String[] args) { Thread t = new Thread(){ @Override public void run() { // 得到的就是t这个引用 ,相当于run中直接使用this System.out.println(Thread.currentThread().getId()); //这是通过this的方法 System.out.println(this.getId()); } }; t.start(); }
上述代码中,两种方法都可以达到这样的目的,但是 this 这种方法只有在继承Thread重写run的方式来创建线程才可以,像实现Runnable接口,lambda这些方式就不可以~~
总结
以上就是我对Thread 类的一些基本用法的描述了,包含了
线程创建
线程中断
线程等待
线程状态
获取线程实例
这几个基本用法,
希望大家多提意见,我也会及时改正,~~
最后
以上就是孤独蓝天最近收集整理的关于Thread 类的基本用法一.线程的创建二.线程中断三.线程等待四.线程的状态五.获取线程的实例总结的全部内容,更多相关Thread内容请搜索靠谱客的其他文章。
发表评论 取消回复