目录
方法名 | 作用 |
---|---|
start() | 让线程进入就绪状态,每个线程对象的start方法只能调用一次 |
run() | 新线程启动后会调用的方法 |
join() | 等待线程运行结束 |
getId() | 获得线程长整型的id |
getName() | 获取线程名 |
getPriority() | 获得线程优先级 |
setPriority() | 修改线程优先级 |
getState() | 获取线程状态 |
isInterrupted() | 判断当前线程是否被打断(不会清除打断标记) |
isAlive() | 线程是否存活 |
interrupt | 打断线程 |
interrupted() | 判断当前线程是否被打断(会清除打断标记) |
currentThread() | 获取当前正在执行的线程 |
sleep() | 让当前执行的线程休眠 |
yield() | 提示线程调度器让出当前线程对cpu的使用 |
1、 run 和 start
package com.java_thread.often_methods;
/**
* run 和 start
*/
import lombok.extern.slf4j.Slf4j;
@Slf4j(topic = "c.RunAndStart")
public class RunAndStart {
public static void main(String[] args) {
Thread thread = new Thread() {
@Override
public void run() {
log.debug("running...");
}
};
//thread.run其实还是主线程,没有起到异步的作用
thread.run();
thread.start();
}
}
2、getState
package com.java_thread.often_methods;
/**
* getState
*/
import lombok.extern.slf4j.Slf4j;
@Slf4j(topic = "c.GetState")
public class GetState {
public static void main(String[] args) {
Thread thread = new Thread() {
@Override
public void run() {
log.debug("running...");
}
};
//尚未启动之前,线程的状态
System.out.println(thread.getState());
//启动之后,线程的状态
thread.start();
System.out.println(thread.getState());
}
}
3、sleep 和 yield
package com.java_thread.often_methods;
/**
* sleep 和 yield
*
* sleep:
* 1、调用sleep会让当前线程从running进入timed waiting状态
* 2、其他线程可以使用interrupt方法打断正在睡眠的线程,这时线程会抛出InterruptedException
* 3、睡眠结束后的线程未必会立刻得到执行
* 4、建议用TimeUnit的sleep代替Thread的sleep来获得更好的可读性
*
* yield:
* 1、调用yield会让当前线程从Running进入runnable就绪状态,然后调度执行其它线程(礼让)
* 2、具体的实现依赖于操作系统的任务调度器(礼让未必成功)
*/
public class SleepAndYield {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread() {
@Override
public void run() {
try {
Thread.sleep(1000 * 2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread.start();
System.out.print("调用sleep方法后,线程从运行变为等待: " + thread.getState() + " -> ");
Thread.sleep(500);
System.out.print(thread.getState() + "\n");
Thread.sleep(500);
thread.interrupt();
System.out.println("调用interrupt后,抛出异常");
}
}
4、setPriority 和 getPriority
package com.java_thread.often_methods;
/**
* setPriority 和 getPriority
*
* 线程优先级:
* 1、线程优先级会提示调度器有限调度该线程,但它仅仅只是一个提示,调度器可以忽略它
* 2、如果cpu很忙,那么优先级高的线程会获得更多的时间片,但cup闲时,优先级几乎没有任何作用
*/
public class SetPriorityAndGetPriority {
public static void main(String[] args) {
Thread t1 = new Thread() {
@Override
public void run() {
int i = 0;
while (true) {
System.out.println("i = " + (i++));
}
}
};
Thread t2 = new Thread() {
@Override
public void run() {
int j = 0;
while (true) {
System.out.println("j = " + (j++));
}
}
};
//越高的优先级被执行的可能越大(不是一定,和cpu的调度有关)
t1.setPriority(10);
t1.start();
t2.setPriority(1);
t2.start();
}
}
5、join
package com.java_thread.often_methods;
import java.awt.*;
import java.util.concurrent.TimeUnit;
/**
* join
*
*/
public class Join {
public static int i = 0;
public static void main(String[] args) throws InterruptedException {
test();
}
private static void test() throws InterruptedException {
Thread t1 = new Thread(() -> {
System.out.println("线程t1开始休眠1秒");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程t1已经醒了");
i = 10;
});
t1.start();
//等待t1线程运行结束,如果没有t1.join()那么i的值为0,有的话为10
t1.join();
System.out.println("i的结果为: " + i);
}
}
没有t1.join()的情况:
有t1.join()的情况
还可以设置时间参数,当时间达到后,线程还没有运行完的话,就不等了,程序继续向下运行
6、interrupt
打断sleep、wait、join的线程
- 打断阻塞的线程,以sleep为例,会清空打断状态
package com.java_thread.often_methods;
/**
* interrupt
*/
public class Interrupt {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
},"t1");
thread.start();
thread.interrupt();
}
}
像wait、sleep、join等被打断后,其打断标志会置为false
package com.java_thread.often_methods;
/**
* interrupt
*/
public class Interrupt {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
},"t1");
thread.start();
Thread.sleep(500);
thread.interrupt();
System.out.println("打断标志: " + thread.isInterrupted());
}
}
-
打断正常运行的线程,不会清空打断标志
Thread thread2 = new Thread(() -> { while (true) { } }, "t2"); thread2.start(); Thread.sleep(1000); thread2.interrupt(); System.out.println("打断标志: " + thread2.isInterrupted());
打断正常运行的线程,不会让线程停止运行,但是可以通过下面方式来使被打断的线程停止运行
Thread thread2 = new Thread(() -> {
while (true) {
boolean interrupted = Thread.currentThread().isInterrupted();
if (interrupted) {
break;
}
}
}, "t2");
7、两阶段终止模式
两阶段终止模式:在一个线程t1中如何优雅的终止线程t2,这里的优雅指的是给t2一个料理后事的机会
7.1 错误思路
-
使用线程对象的stop方法停止线程
stop方法会真正杀死线程,如果这是线程锁住了共享资源,那么当它被杀死后,就再也没有机会释放锁,其他线程将永远无法获取锁
-
使用System.exit(int) 方法停止线程
目的仅是停止一个线程,但这种做法会让整个程序都停止
7.2 正确思路
package com.java_thread.often_methods;
/**
* 两阶段终止模式
*/
public class TwoPhaseTermination {
private Thread monitor;
//启动监控线程
public void start() {
monitor = new Thread(() -> {
while (true) {
Thread current = Thread.currentThread();
if (current.isInterrupted()) {
System.out.println("料理后事");
break;
}
try {
//1、如果处在睡眠转态被打断,那么会执行catch块中的current.interrupt(),重新设置打断标志
Thread.sleep(1000);
//2、如果处于没有睡眠的时候被打断,那么打断标志还是true
System.out.println("执行监控");
} catch (InterruptedException e) {
e.printStackTrace();
//重新设置打断标志变为true
current.interrupt();
}
}
});
monitor.start();
}
public void stop() {
monitor.interrupt();
}
}
class Test {
public static void main(String[] args) throws InterruptedException {
TwoPhaseTermination t = new TwoPhaseTermination();
t.start();
Thread.sleep(3500);
t.stop();
}
}
总结:初始打断标记为true,调用打断方法interrupt()执行打断操作,如果被打断的是没有睡眠的线程,那么打断标记还是true,如果是像wait、sleep、join等被打断后,其打断标志会置为false。
isInterrupted()可以查看当前的打断标记,但是不会重新设置打断标记。
interrupted()会重新设置打断标记
8、打断park线程
当线程执行到LockSupport时就会停止执行,可以通过下面方式来打断park线程
package com.java_thread.often_methods;
import java.util.concurrent.locks.LockSupport;
/**
* 打断park线程
*/
public class park {
public static void main(String[] args) throws InterruptedException {
test();
}
private static void test() throws InterruptedException {
Thread thread = new Thread(() -> {
System.out.println("park...");
LockSupport.park();
System.out.println("unpark...");
},"t1");
thread.start();
Thread.sleep(1);
thread.interrupt();
}
}
但是当再次执行park时,发现不起作用
Thread thread = new Thread(() -> {
System.out.println("park...");
LockSupport.park();
System.out.println("unpark...");
LockSupport.park();
},"t1");
此时可以设置重新设置打断标记来使其发生变化
Thread thread = new Thread(() -> {
System.out.println("park...");
LockSupport.park();
System.out.println("unpark...");
Thread.interrupted();
LockSupport.park();
},"t1");
从上图可以发现,程序一直运行在第二次设置LockSupport.park()的位置
9、不推荐使用的方法
还有一些不推荐使用的方法,这些方法已经过时,容易破坏同步代码块,造成线程死锁
方法名 | 功能说明 |
---|---|
stop() | 停止线程运行 |
suspend() | 挂起(暂停)线程运行 |
resume() | 恢复线程运行 |
10、主线程和守护线程
默认情况下,Java进程需要等待所有线程都运行结束,才会结束。有一种特殊的线程叫做守护线程
,只要其他非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制执行结束。
package com.java_thread.often_methods;
/**
* 守护线程
*/
public class DaemonThread {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
while (true) {
if (Thread.currentThread().isInterrupted()) {
break;
}
}
System.out.println("t1线程结束");
},"t1");
thread.setDaemon(true);
thread.start();
Thread.sleep(1000);
System.out.println("主线程结束");
}
}
从上可以发现,t1线程没有执行结束,但是程序的运行还是结束了。说明的查询的运行是和守护线程的运行是否完成没有关系的。
注意:
- 垃圾回收线程就是一种守护线程
- Tomcat中的Acceptor和Poller线程都是守护线程,所以Tomcat接收到shutdown命令后,Tomcat不会等待他们完成之后再关闭。