线程的常用操作方法,大都定义在Thread类中,包括一些重要的静态方法和实例方法。下面我们通过这篇文章来了解一些比较常用的方法。
sleep
sleep的作用是让当前正在执行的线程休眠。让cpu去执行其它的线程。
执行sleep方法后,线程的状态变为 TIME_WAITING (超时等待)。
public class Demo002_d {
public static void main(String[] args) {
testSleep();
while (true){
}
}
/**
*
* @desc sleep 方法学习
* */
public static void testSleep(){
new Thread("testSleep"){
@Override
public void run() {
try {
// testSleep 线程休眠2s
Thread.sleep(200000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
}
interrupt
interrupt的作用是用来中断线程,其被设计用来替代stop()方法。
Thread.stop()用来停止线程,在JDK中已经被标记为过时方法;其最大的弊端在于强制终止正在运行的线程,相当于关闭计算机电源。如果正在运行的线程持有锁资源,那么会导致锁资源永远无法释放。所以JDK已经不推荐使用Thread.stop()来停止进程。
那么一个线程应该怎么样正确的终止线程了?当然,这个只有当事线程才知道。interrupt的设计正是于此,它不会终止线程,只是作为一个中断标识,告诉当前线程,父线程想要终止当前线程。至于怎样终止,则取决于当前线程本身。
中断阻塞线程
如果当前线程处于阻塞状态,则interrupt会让当前线程抛出InterruptedException异常;用户可以通过捕获InterruptedException异常来做一些处理。
public class Demo002_d {
public static void main(String[] args) {
Thread t1 = testinterrupt();
t1.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//main线程休眠2秒之后,中断t1线程
t1.interrupt();
}
/**
* @desc interrupt 中断休眠的线程
* */
public static Thread testinterrupt(){
return new Thread("testinterrupt"){
@Override
public void run() {
try {
Thread.sleep(200000);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("哎,线程被中断了!!");
}
}
};
}
}
运行结果如下:
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.java.basic.thread.demo001.Demo002_d$1.run(Demo002_d.java:29)
哎,线程被中断了!!
中断正在运行的线程
如果线程正处于运行状态,线程不受任何影响;仅仅是线程的中断标记被设置为true,所以用户需要通过isInterrupted()获取中断标识,执行退出操作。
public class Demo002_d {
public static void main(String[] args) {
Thread t1 = testinterrupt1();
t1.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//main线程休眠2秒之后,中断t1线程
t1.interrupt();
}
/**
* @desc interrupt 中断正在运行的线程
* */
public static Thread testinterrupt1(){
return new Thread("testinterrupt"){
@Override
public void run() {
while(true){
if(isInterrupted()){
System.out.println("线程被中断了...");
return;
}
}
}
};
}
}
join
join的作用是线程合并。
感觉线程的join和sql的join是比较类似的。假设有两个线程A和B,B线程的执行需要依赖A线程执行完成。join就是为这种场景设计的。join支持下面两种方式:
1、t.join() : 线程进入 WAITING 状态,直到t线程执行完毕,才能执行下面的代码。
2、t.join(2000) : 线程进入 TIME_WAITING 状态,主线程仅仅等待t线程执行 2000 毫秒,无论t线程是否执行完毕,都会执行下面的代码。
public class Demo002_d {
public static void main(String[] args) {
try {
testJoin();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
*
* @desc 线程合并测试
* */
public static void testJoin() throws InterruptedException {
Thread a = new Thread("A"){
@Override
public void run() {
System.out.println("A 线程正在执行....");
}
};
a.start();
a.join();
Thread b = new Thread("B"){
@Override
public void run() {
System.out.println("B 线程正在执行....");
}
};
b.start();
b.join();
System.out.println("main 线程正在结束....");
}
}
运行结果如下:
A 线程正在执行…
B 线程正在执行…
main 线程正在结束…
如上所示,无论你执行多少次,线程B都是在线程A结束之后才执行的。
yield
yield 会出让cpu的执行权限,出让执行权限之后,该线程进入RUNNABLE状态。重新加入cpu时间片段的竞争。
这里A、B线程每执行一次输出操作,就出让CPU执行权限,然后重新竞争CPU时间片。
public class Demo002_d {
public static void main(String[] args) {
testYield();
}
/**
*
* @desc yield 测试
* */
public static void testYield(){
Thread a = new Thread("A"){
@Override
public void run() {
for(int i=0;i<5;i++) {
System.out.println("A 线程正在执行....");
Thread.yield();
}
}
};
Thread b = new Thread("B"){
@Override
public void run() {
for(int i=0;i<5;i++) {
System.out.println("B 线程正在执行....");
Thread.yield();
}
}
};
a.start();
b.start();
}
}
守护线程
Java中的线程分为2类,用户线程和守护线程;他们的区别是:
1、用户线程:当所有用户线程执行完成后,JVM进程才会停止;
2、守护线程: 当JVM进程停止的时候,守护线程就会停止;所以在守护线程中尽量不要去访问系统资源、连接数据库等。GC垃圾回收线程就是守护线程。
public class Demo002_d {
public static void main(String[] args) {
testDeamon();
//主线程暂停2s
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程结束...");
}
/**
* @desc 守护线程测试
* */
public static void testDeamon(){
Thread deamon = new Thread("testDeamon"){
@Override
public void run() {
while(true){
System.out.println("守护线程运行中......");
}
}
};
//设置为守护线程
deamon.setDaemon(true);
deamon.start();
}
}
可以观察到,当主线程结束之后,守护线程立马结束,虽然我们没有去停止它。
需要注意的是:
1、守护线程必须在线程启动之前调用 setDaemon(true);
2、守护线程创建的线程也是守护线程;
线程设计模式(二阶段终止)
在java中怎么样终止线程了?
1、使用stop()方法停止线程: stop()方法会真正杀死线程,如果此时该线程持有锁,那么其他线程将永远无法获取锁。
2、使用System.exit()方法停止线程:会让整个进程都退出。
3、使用interrupt 两阶段终止:使用interrupt将线程的终止权力交给线程本身,本来是没有问题的。问题在于当被打断的线程处于阻塞状态的时候,阻塞线程会抛出InterruptedException,并将中断标识清空,无法停止线程。所以我们需要捕获InterruptedException异常,并重新将打断标识设置为true。
为了处理阻塞线程清空中断标识的问题,我们不得不在 catch 中重新打断。所以称为二阶段终止。
public class Demo002_d {
public static void main(String[] args) {
testTwoPhaseTermination();
}
public static void testTwoPhaseTermination(){
Thread t1 = new Thread("testTwoPhaseTermination"){
@Override
public void run() {
while(true){
if(isInterrupted()){
System.out.println("线程要关闭了...");
break;
}
try {
// 如果在阶段1被打断,会进入catch语句块,并且isInterrupted标志位清空,无法关闭线程
Thread.sleep(1000); // 阶段1
// 如果在阶段2被打断,线程的isInterrupted标志位为true,会捕抓到信号并关闭线程
System.out.println("监控线程正在工作...."); // 阶段2
} catch (InterruptedException e) {
e.printStackTrace();
// 需要重新设置isInterrupted标志位为true
interrupt();
}
}
}
};
t1.start();
//主线程停止3秒
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//打断t1线程
t1.interrupt();
}
}