线程经常使用操作
2017-08-07 14:48 tlnshuju 阅读(192) 评论(0) 编辑 收藏 举报本文解说了Java中中断线程的概念和经常用法以及让步操作和优先级等概念。对线程的经常使用操作进行初步了解。
1 停止线程
Java停止一个线程是通过调用Thread类的interrupt()
方法来实现的。以下详细讲一下它的相关用法。
1.1 interrupt() != 马上终止
调用interrupt()
并不会像break语句那样直接就终止线程。它不过在当前线程中打了一个停止的标记,并非真的停止线程。
国际惯例,上栗子:
自己定义线程类
package com.trigl.concurrent.simplethread;
/**
* 用于解说interrupt()方法的线程类
* @author 白鑫
* @date 2016年3月31日 上午11:39:04
*/
public class InterruptThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 500000; i++) {
System.out.println("i=" + (i + 1));
}
}
}
測试方法
/**
* 先让线程启动,线程会打印50万条数据,会花费一定时间。
* 所以让test方法休眠1秒,再停止线程。
* @throws InterruptedException
*/
@Test
public void testInterruptThread() throws InterruptedException {
InterruptThread thread = new InterruptThread();
thread.start();
Thread.sleep(1000);
thread.interrupt();
System.out.println("end");
}
输出结果
从结果看并没有打印到i=500000。貌似是interrupt()方法起作用了,事实上非也。关键在:
Thread.sleep(1000);
这条代码上,它将当前test方法的线程休眠了1秒钟。在这一秒中之内线程thread打印到了图中所看到的的最后一条i=123878,然后test方法运行完毕了就结束了。而thread线程还在后台运行。只不过因为test方法结束了。所以不再打印到控制台上。假设我们将休眠时间改为10秒,即改为:
Thread.sleep(10000);
再来看一下结果:
能够看到,50万条数据所有打印了出来,所以interrupt()
方法并没有起作用。
那么问题来了。究竟怎么才干真的停止线程呢?
1.2 推断线程是否是停止状态
在介绍怎样停止线程之前,首先看一下怎样推断线程的停止状态。Thread类提供了两种方法:
public static boolean interrupted()
// 測试当前线程是否已经中断public boolean isInterrupted()
// 測试线程是否已经中断
1.2.1 interrupted()
interrupted()是一个静态方法。通过
Thread.interrupted();
来直接直接调用,它推断的是当前正在运行的线程的中断状态。上栗子:
測试方法
/**
* 測试Thread的interrupted()方法
*/
@Test
public void testInterrupted() {
Thread.currentThread().interrupt();
System.out.println("Thread.interrupted()" + Thread.interrupted());
System.out.println("Thread.interrupted()" + Thread.interrupted());
System.out.println("end");
}
输出结果
Thread.interrupted()=true
Thread.interrupted()=false
end
结果非常奇怪,明明是两条同样的代码。为什么结果一个是true一个是false呢?
从官方API中找答案:
boolean java.lang.Thread.interrupted()
Tests whether the current thread has been interrupted. The interrupted status of the thread is cleared by this method. In other words, if this method were to be called twice in succession, the second call would return false (unless the current thread were interrupted again, after the first call had cleared its interrupted status and before the second call had examined it).
A thread interruption ignored because a thread was not alive at the time of the interrupt will be reflected by this method returning false.
说的非常明确:假设连续两次调用该方法,则第二次调用将返回false(除非正好在第二次调用前当前线程又中断了一次);假设线程已经不再存活(not alive)。那么将忽略中断状态标记,调用该方法会返回false。
1.2.2 isInterrupted()
该方法推断的是一个Thread对象的中断状态。而不是当前线程的中断状态。上栗子来比較与interrupted()
方法的不同:
測试方法
/**
* 測试Thread的isInterrupted()方法
* @throws InterruptedException
*/
@Test
public void testIsInterrupted() throws InterruptedException {
InterruptThread thread = new InterruptThread();
thread.start();
thread.interrupt();
System.out.println("thread.isInterrupted()=" + thread.isInterrupted());
System.out.println("thread.isInterrupted()=" + thread.isInterrupted());
System.out.println("end");
}
输出结果
thread.isInterrupted()=true
i=1
thread.isInterrupted()=true
end
i=2
i=3
......
从结果中能够看到,方法isInterrupted()
并未清除状态标志,所以打印了两个true。
1.3 使用异常法停止线程
详细实现就是在一个自己定义线程类的run方法中,首先推断一下线程是否是停止状态,假设是停止状态,则手动抛出一个InterruptedException
异常来中断线程,实比例如以下:
自己定义线程类
package com.trigl.concurrent.simplethread;
/**
* 使用异常法停止线程
* @author 白鑫
* @date 2016年3月31日 上午11:39:04
*/
public class InterruptThread extends Thread {
@Override
public void run() {
super.run();
try {
for (int i = 0; i < 500000; i++) {
if (Thread.interrupted()) {
System.out.println("已经是停止状态了!
我要退出了!");
throw new InterruptedException();
}
System.out.println("i=" + (i + 1));
}
System.out.println("假设你能看到我说明线程并没有真的被中断!");
} catch (Exception e) {
// TODO: handle exception
System.out.println("进入到InterruptThread类run方法中的catch了。");
e.printStackTrace();
}
}
}
測试方法
@Test
public void testInterruptThread() throws InterruptedException {
InterruptThread thread = new InterruptThread();
thread.start();
Thread.sleep(1000);
thread.interrupt();
System.out.println("end");
}
输出结果
......
i=121981
i=121982
i=121983
end
已经是停止状态了!我要退出了!
进入到InterruptThread类run方法中的catch了!
java.lang.InterruptedException
at com.trigl.concurrent.simplethread.InterruptThread.run(InterruptThread.java:16)
1.4 在沉睡中停止线程
分为两种。还有一种是先休眠后停止线程,一种是先停止线程后休眠。这两种都会抛出java.lang.InterruptedException
的错误
1.4.1 先休眠后停止
自己定义线程类
package com.trigl.concurrent.simplethread;
/**
* 先休眠后停止线程
* @author 白鑫
* @date 2016年4月1日 上午10:33:11
*/
public class InterruptedWhenSleep extends Thread {
@Override
public void run() {
super.run();
try {
System.out.println("run begin");
Thread.sleep(20000);
System.out.println("run end");
} catch (InterruptedException e) {
// TODO: handle exception
System.out.println("在沉睡中被停止!
进入catch!isInterrupted()=" + this.isInterrupted());
e.printStackTrace();
}
}
}
測试方法
@Test
public void testInterruptedWhenSleep() throws InterruptedException {
InterruptedWhenSleep thread = new InterruptedWhenSleep();
thread.start();
Thread.sleep(200); // 休眠在中断前面,保证thread先休眠再中断
thread.interrupt();
}
输出结果
run begin
在沉睡中被停止!进入catch!isInterrupted()=false
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.trigl.concurrent.simplethread.InterruptedWhenSleep.run(InterruptedWhenSleep.java:14)
1.4.2 先停止后休眠
自己定义线程类
package com.trigl.concurrent.simplethread;
/**
* 先停止后休眠线程
* @author 白鑫
* @date 2016年4月1日 上午10:38:08
*/
public class InterruptedWhenSleep1 extends Thread {
@Override
public void run() {
super.run();
try {
for (int i = 0; i < 100000; i++) {
System.out.println("i=" + (i + 1));
}
System.out.println("run begin");
Thread.sleep(20000);
System.out.println("run end");
} catch (InterruptedException e) {
System.out.println("先停止,再遇到了sleep!
进入catch!");
e.printStackTrace();
}
}
}
測试方法
@Test
public void testInterruptedWhenSleep1() throws InterruptedException {
InterruptedWhenSleep1 thread = new InterruptedWhenSleep1();
thread.start();
thread.interrupt();
Thread.sleep(1000); // 加这个休眠是为了延迟test方法的完毕,从而thread的打印报错信息
System.out.println("end");
}
输出结果
......
i=99999
i=100000
run begin
先停止,再遇到了sleep!进入catch!
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.trigl.concurrent.simplethread.InterruptedWhenSleep1.run(InterruptedWhenSleep1.java:17)
end
1.5 暴力停止线程
能够使用Thread类的stop()方法暴力停止线程,可是这种方法是不安全的。并且是已被弃用作废的(deprecated),最好不要使用。这个样例看看就能够了。反正没人会用这种方法233333。
自己定义线程类
package com.trigl.concurrent.simplethread;
/**
* 用stop()方法暴力停止线程
* @author 白鑫
* @date 2016年4月1日 上午10:50:51
*/
public class StopThread extends Thread {
private int i = 0;
@Override
public void run() {
try {
while (true) {
i++;
System.out.println("i=" + i);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
測试方法
@SuppressWarnings("deprecation")
@Test
public void testStopThread() throws InterruptedException {
StopThread thread = new StopThread();
thread.start();
Thread.sleep(5000);
thread.stop();
}
输出结果
i=1
i=2
i=3
i=4
i=5
1.6 使用return停止线程
将方法interrupt()和return结合使用也能够停止线程。
自己定义线程类
package com.trigl.concurrent.simplethread;
/**
* 使用return停止线程
* @author 白鑫
* @date 2016年4月1日 下午3:21:16
*/
public class UseReturnInterrupt extends Thread {
@Override
public void run() {
while (true) {
if (this.isInterrupted()) {
System.out.println("停止了!");
return;
}
System.out.println("timer=" + System.currentTimeMillis());
}
}
}
測试方法
@Test
public void testUseReturnInterrupt() throws InterruptedException {
UseReturnInterrupt thread = new UseReturnInterrupt();
thread.start();
Thread.sleep(1000);
thread.interrupt();
System.out.println("end");
}
输出结果
......
timer=1459495136740
timer=1459495136740
timer=1459495136740
停止了。
end
2 线程的让步操作
线程使用yield()
方法来放弃当前的CPU资源,将它让给其它任务去占用CPU运行时间。但放弃的时间不确定,有可能刚刚放弃。马上又获得CPU时间片。
上栗子~
自己定义线程类
package com.trigl.concurrent.simplethread;
/**
* 线程放弃CPU资源
* @author 白鑫
* @date 2016年4月1日 下午3:54:18
*/
public class YieldThread extends Thread {
@Override
public void run() {
long beginTime = System.currentTimeMillis();
int count = 0;
for (int i = 0; i < 50000000; i++) {
count = count + (i + 1);
// Thread.yield(); // 主动放弃CPU资源,将这行代码凝视和不凝视分别跑一下,看一下区别
}
long endTime = System.currentTimeMillis();
System.out.println("用时:" + (endTime - beginTime) + "毫秒!");
}
}
測试方法
@Test
public void testYieldThread() throws InterruptedException {
YieldThread thread = new YieldThread();
thread.start();
Thread.sleep(10000);
}
不调用yield()
方法输出结果
用时:1毫秒!
调用yield()
方法输出结果
用时:4255毫秒!
能够看到。处理5千万自加1操作,调用yield()
和不调用yield()
方法区别还是非常大的,相差了近4000倍。
3 线程的优先级
在操作系统中,线程能够划分优先级,优先级高的线程得到的CPU资源多。也就是CPU优先运行优先级较高的线程对象中的任务。
设置优先级使用setPriority()
方法。得到优先级使用getPriority()
方法。
在Java中,线程的优先级分为1~10这10个等级,假设小于1或者大于10,则JDK抛出IllegalArgumentException
异常。
JDK中使用3个常量来预置定义优先级的值。代码例如以下:
- public final static int MIN_PRIORITY = 1;
- public final static int NORM_PRIORITY = 5;
- public final static int MAX_PRIORITY = 10;
在Java中。线程的优先级具有继承性,比方A相称启动了B线程,则B线程的优先级与A是一样的。
上栗子:
自己定义线程类
package com.trigl.concurrent.simplethread;
/**
* 线程的优先级的继承特性
* @author 白鑫
* @date 2016年4月1日 下午4:48:28
*/
public class InheritPriorityThread extends Thread {
@Override
public void run() {
System.out.println("InheritPriorityThread run priority=" + this.getPriority());
}
}
測试方法
@Test
public void testInheritPriorityThread() {
System.out.println("test thread begin priority=" + Thread.currentThread().getPriority());
// Thread.currentThread().setPriority(6); // 设置当前线程的优先级
System.out.println("test thread end priority=" + Thread.currentThread().getPriority());
InheritPriorityThread thread = new InheritPriorityThread();
thread.start();
}
输出结果
test thread begin priority=5
test thread end priority=5
InheritPriorityThread run priority=5
将代码:
// Thread.currentThread().setPriority(6); // 设置当前线程的优先级
前的凝视去掉,再次运行,得到结果例如以下:
test thread begin priority=5
test thread end priority=6
InheritPriorityThread run priority=6
能够看到,因为在test方法中启动了还有一个线程。所以他们的线程优先级同样。都是6.
OVER