线程常用方法
线程常用方法介绍
1、方法概述
方法说明 | static | 功能说明 | 注意 |
---|---|---|---|
start() | 启动一个新线程, 在新的线程运行 run方法中的代码 |
start方法只是让线程进入就绪,里面代码不一定立刻运行 (CPU的时间片还没分给它)。每个线程对象的start方法只 能调用一次,如果调用了多次会出现 IllegalThreadStateException |
|
run() | 新线程启动后会调 用的方法 |
如果在构造Thread对象时传递了Runnable参数,则线程启 动后会调用Runnable中的run方法,否则默认不执行任何操 作。但可以创建Thread的子类对象,来覆盖默认行为 |
|
join() | 等待线程运行结束 | ||
join(long n) | 等待线程运行结束, 最多等待n毫秒 |
||
getId() | 获取线程id | id唯一 | |
getName() | 获取线程名 | ||
setName(String) | 修改线程名 | ||
getPriority() | 获取线程优先级 | ||
setPriority(int) | 修改线程优先 | java中规定线程优先级是1~10的整数,默认是5,较大的优先级 能提高该线程被CPU调度的机率 |
|
getState() | 获取线程状态 | Java中线程状态是用6个enum表示,分别为:NEW RUNNABLE、BLOCKED、WAITING、TIMED_WAITING. TERMINATED |
|
isInterrupted() | 判断是否被打断, | 不会清除打断标记 | |
isAlive() | 线程是否存活 (还 没有运行完毕) |
||
interrupt() | 打断线程 | 如果被打断线程正在sleep,wait,join会导致被打断的线程 抛出InterruptedException,并清除打断标记;如果打断的正 在运行的线程,则会设置打断标记;park的线程被打断,也 会设置打断标记 |
|
interrupted() | static | 判断当前线程是否 被打断 |
会清除打断标记 |
currentThread() | static | 获取当前正在执行 的线程 |
|
sleep(long n) | static | 让当前执行的线程 休眠n毫秒,休眠时 让出cpu的时间片 给其它线程 |
|
yield() | static | 提示线程调度器让 出当前线程对CPU 的使用 |
主要是为了测试调试 |
2、start() vc run()
start方法会启动一个线程,并执行实现的run方法,多次执行会抛异常:IllegalThreadStateException
单独调用run方法,会当作main线程中的一个普通线程
public static void main(String[] args) {
Thread t = new Thread(() -> {
log.debug("hello world");
}, "t");
t.run();
t.start();
t.start();//start()方法多次启动会抛异常IllegalThreadStateException
}
3、sleep vs yield
1、sleep
1.调用sleep会让当前线程从Runnable进入Timed_Waiting状态
2.其它线程可以使用interrupt方法打断正在睡眠的线程,这时sleep方法会抛出InterruptedException
3.睡眠结束后的线程未必会立刻得到快行
4.建议用TimeUnit的sleep代替Thread的sleep来获得更好的可读性
2、yield
1.调用yield会上当前线程从Running进入Runnable状态,然后调度执行其它同优先级的线程。如果这时没
有同优先级的线程,那么不能保证让当前线程暂停的效果
2.具体的实现依赖于操作系统的任务调度器
3、线程优先级
-
线程优先级会提示(hit)调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它
-
如果CPU比较忙,那么优先级高的线程会获得更多的时间片,但CPU闲时,优先级几乎没作用
-
优先级1-10,默认为5
4、示例
打断正在sleep、wait、join的线程,清楚打断标记,并抛出异常
public static void main(String[] args) {
Thread t = new Thread(() -> {
try {
log.debug("run...");
TimeUnit.SECONDS.sleep(1);//还是使用了Thread.sleep(),线程执行1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t");
t.start();
log.debug("state1:{}", t.getState());
try {
Thread.sleep(500);//线程执行0.5秒,不停顿,容易导致t线程还没开始执行执行下面的语句:t.getState()等等,结果会不准确
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("state2:{}", t.getState());
//打断正在sleep、wait、join的线程,清楚打断标记
log.debug("interrupt...");
t.interrupt();
log.debug("打断标记:{}", t.isInterrupted());
}
yield会让出CPU,测试优先级和yield
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
int count = 0;
while (true) {
Thread.yield();
System.out.println("t1 ---->" + count++);
}
});
Thread t2 = new Thread(() -> {
int count = 0;
while (true) {
System.out.println(" t2 ---->" + count++);
}
});
// t1.setPriority(Thread.MIN_PRIORITY);//优先级1
// t2.setPriority(Thread.MAX_PRIORITY);//优先级10
t1.start();
t2.start();
}
使用yield
使用优先级(注释 Thread.yield()😉,设置t1、t2的优先级
5、sleep防止CPU占用100%
CPU浪费(一核CPU的Linux)
public class Methods {
public static void main(String[] args) {
new Thread(() -> {
while (true) {
//try {Thread.sleep(1);} catch (InterruptedException e) {}
}
}).start();
}
}
去掉注释,使用sleep避免while(true)空转浪费CPU
- 可以用wt或条件变量达到类似的效果
- 不同的是,后两种都需要加锁,并且需要相应的唤醒操作,一般适用于要进行同步的场景
- sleep适用于无需锁同步的场景
4、join
join,相当于插队,必须等该线程执行完,当前线程才能继续向下执行
示例,main线程需要t线程来计算结果,如果不等t线程计算完,就输出结果,会导致结果不正确
static int result = 0;
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
try {
log.debug("开始计算...");
Thread.sleep(1000);
result = 10;
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("计算完成...");
}, "t");
t.start();
//t.join();
log.debug("结果:{}", result);
}
加入t.join()
使用join(long millis),超时了不会抛出异常
5、interrupt
1、打断阻塞线程
打断阻塞线程,会清楚打断标记(标记线程是否被打断过)
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
log.debug("hi");
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t");
t.start();
TimeUnit.MILLISECONDS.sleep(500);//相当于Thread.sleep(long millis);
log.debug("开始打断...");
t.interrupt();
log.debug("打断标记 {}", t.isInterrupted());
}
2、打断运行
打断运行中的线程,打断标记为true
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
while (true) {
boolean interrupted = Thread.currentThread().isInterrupted();
if (interrupted) {
log.debug("打断运行,推出循环");
break;
}
}
}, "t");
t.start();
log.debug("开始打断...");
t.interrupt();
log.debug("打断标记 {}", t.isInterrupted());
}
6、两阶段终止模式
Two Phase Termination
在一个线程t1中如何优雅终止线程t2?这里的【优推】指的是给t2一个料理后事的机会。
1.错误思路
-
使用线程对象的stop()方法停止线程,
stop方法会真正杀死线程,如果这时线程锁住了共享资源,那么当它被杀死后就再也没有机会释放锁,其它线程将永远无法获取锁
-
使用System.exit(int)方法停I止线程
目的仅是停止一个线程,但这种做法会让整个程序都停止
2、思路
3、示例
如果被打断线程正在sleep,wait,join会导致被打断的线程抛出InterruptedException,并清除打断标记;如果打断的正在运行的线程,则会设置打断标记;park的线程被打断,也会设置打断标记
public static void main(String[] args) throws InterruptedException {
TwoPhaseTermination t = new TwoPhaseTermination();
t.start();
Thread.sleep(3500);
t.stop();
}
static class TwoPhaseTermination{
private Thread monitor;
//启动监控现场
public void start(){
monitor = new Thread(() -> {
while (true){
boolean interrupted = Thread.currentThread().isInterrupted();
if(interrupted){
log.debug("线程终止,料理后事");
break;
}
try {
log.debug("监控记录");
Thread.sleep(1000);
} catch (InterruptedException e) {
//重新设置打断标记,因为打断的是sleep中的线程,会清楚打断标记
//没有该语句打断后会继续打印 监控记录
monitor.interrupt();
e.printStackTrace();
}
}
}, "monitor");
monitor.start();
}
//停止监控线程
public void stop(){
monitor.interrupt();
}
}
本文来自博客园,作者:如梦幻泡影,转载请注明原文链接:https://www.cnblogs.com/WangJiQing/p/16989271.html