之前的文章介绍了java线程的背景由来,还有创建线程的方法。本篇把线程的常用方法梳理一下,作为补充。
线程睡眠——sleep
如果我们需要让当前正在执行的线程暂停一段时间,并进入阻塞状态,则可以通过调用Thread的sleep方法,从上面可以看到sleep方法有两种重载的形式,但是使用方法一样。
sleep(long millis,int nanoseconds) //第一参数为毫秒,第二个参数为纳秒
package thread;
public class Test1 {
private int i = 10;
private Object object = new Object();
public static void main(String[] args) {
Test1 test = new Test1();
MyThread thread1 = test.new MyThread();
MyThread thread2 = test.new MyThread();
thread1.start();
thread2.start();
}
class MyThread extends Thread{
@Override
public void run() {
synchronized (object) {
i++;
System.out.println("i:"+i);
try {
System.out.println("线程"+Thread.currentThread().getName()+"进入睡眠状态");
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
// TODO: handle exception
System.out.println(e.getStackTrace());
}
System.out.println("线程"+Thread.currentThread().getName()+"睡眠结束");
i++;
System.out.println("i:"+i);
}
}
}
}
运行结果:
从上面输出结果可以看出,当Thread-0进入睡眠状态之后,Thread-1并没有去执行具体的任务。只有当Thread-0执行完之后,此时Thread-0释放了对象锁,Thread-1才开始执行。
注意,如果调用了sleep方法,必须捕获InterruptedException异常或者将该异常向上层抛出。当线程睡眠时间满后,不一定会立即得到执行,因为此时可能CPU正在执行其他的任务。所以说调用sleep方法相当于让线程进入阻塞状态。
注意点2:sleep是Thread的静态类方法,谁调用的谁去睡觉,即使在a线程里调用了b的sleep方法,实际上还是a去睡觉,要让b线程睡觉要在b的代码中调用sleep。public class Test1 {
private Object object = new Object();
private int i = 10;
/**
* @param args
*/
public static void main(String[] args) throws InterruptedException {
System.out.println(Thread.currentThread().getName());
MyThread myThread=new Test1().new MyThread();
myThread.start();
myThread.sleep(1000);//这里sleep的就是main线程,而非myThread线程
for(int i=0;i<100;i++){
System.out.println("main"+i);
}
}
class MyThread extends Thread{
@Override
public void run() {
synchronized (object) {
i++;
System.out.println("i:"+i);
try {
System.out.println("线程"+Thread.currentThread().getName()+"进入睡眠状态");
Thread.currentThread().sleep(100);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println("线程"+Thread.currentThread().getName()+"睡眠结束");
i++;
System.out.println("i:"+i);
}
}
}
}
运行结果:
main
i:11
线程Thread-0进入睡眠状态
线程Thread-0睡眠结束
i:12
main0
main1
main2
main3
main4
。。。
3)sleep与调度准确性
因为使用sleep方法之后,线程是进入阻塞状态的,只有当睡眠的时间结束,才会重新进入到就绪状态,而就绪状态进入到运行状态,是由系统控制的,我们不可能精准的去干涉它,所以如果调用Thread.sleep(1000)使得线程睡眠1秒,可能结果会大于1秒。可以自己试试,就不写例子了。
2yield方法
public class Test2 {
public static void main(String[] args) throws InterruptedException {
Test2 test = new Test2();
test.new MyThread("低级", 1).start();
test.new MyThread("中级", 5).start();
test.new MyThread("高级", 10).start();
}
class MyThread extends Thread {
public MyThread(String name, int pro) {
super(name);// 设置线程的名称
this.setPriority(pro);// 设置优先级
}
public void run() {
for (int i = 0; i < 4; i++) {
System.out.println(this.getName() + "线程第" + i + "次执行!");
if (i % 2 == 0)
Thread.yield();
}
}
}
}
关于sleep()方法和yield()方的区别如下:
①、sleep方法暂停当前线程后,会进入阻塞状态,只有当睡眠时间到了,才会转入就绪状态。而yield方法调用后 ,是直接进入就绪状态,所以有可能刚进入就绪状态,又被调度到运行状态。
②、sleep方法声明抛出了InterruptedException,所以调用sleep方法的时候要捕获该异常,或者显示声明抛出该异常。而yield方法则没有声明抛出任务异常。
③、sleep方法比yield方法有更好的可移植性,通常不要依靠yield方法来控制并发线程的执行。
3join方法
关于join的含义,有人说是“将几个并行线程的线程合并为一个单线程执行”,我个人觉得“等待该线程终止”更合适,也就是在线程调用了join()方法后面的代码,只有等到线程结束了才能执行。
join方法有三个重载版本:
1
2
3
|
join()
join(
long
millis)
//参数为毫秒
join(
long
millis,
int
nanoseconds)
//第一参数为毫秒,第二个参数为纳秒
|
public class Test3 {
<span style="white-space:pre"> </span>public static void main(String[] args) throws InterruptedException {
<span style="white-space:pre"> </span>MyThread thread=new MyThread();
<span style="white-space:pre"> </span>thread.start();
<span style="white-space:pre"> </span>thread.join();//将主线程加入到子线程后面
<span style="white-space:pre"> </span>for(int i=0;i<10;i++){
<span style="white-space:pre"> </span>System.out.println(Thread.currentThread().getName() + "线程第" + i + "次执行!");
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>class MyThread extends Thread {
<span style="white-space:pre"> </span>@Override
<span style="white-space:pre"> </span>public void run() {
<span style="white-space:pre"> </span>for (int i = 0; i < 1000; i++) {
<span style="white-space:pre"> </span>System.out.println(Thread.currentThread().getName() + "线程第" + i + "次执行!");
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
运行结果:
4.interrupt方法
上面文章说过如何中断一个线程,就是单独调用interrupt方法可以使得处于阻塞状态的线程抛出一个异常,也就说,它可以用来中断一个正处于阻塞状态的线程;另外,通过interrupt方法和isInterrupted()方法来停止正在运行的线程。
我们看看sleep、wait、join方法的声明:
- public final void wait() throws InterruptedException
- public static native void sleep(long millis) throws InterruptedException
- public final void join() throws InterruptedException
这三者有一个共同点,都抛出了一个InterruptedException的异常。
每个Thread都有一个中断状状态,默认为false。可以通过Thread对象的isInterrupted()方法来判断该线程的中断状态。可以通过Thread对象的interrupt()方法将中断状态设置为true。当一个线程处于sleep、wait、join这三种状态之一的时候,如果此时他的中断状态为true,那么它就会抛出一个InterruptedException的异常,并将中断状态重新设置为false。
public class Test4 {
public static void main(String[] args) {
Test4 test = new Test4();
MyThread thread = test.new MyThread();
thread.start();
}
class MyThread extends Thread{
@Override
public void run() {
try {
System.out.println("进入睡眠状态");
Thread.sleep(1000);
this.interrupt();
System.out.println("睡眠完毕");
} catch (InterruptedException e) {
System.out.println("得到中断异常");
}
System.out.println("run方法执行完毕");
}
}
}
输出如下:
进入睡眠状态
睡眠完毕
run方法执行完毕
从这里可以看出,通过interrupt方法可以中断处于阻塞状态的线程。
当然停止线程还是推荐:使用共享变量(shared variable)发出信号,告诉线程必须停止正在运行的任务。线程必须周期性的核查这一变量(尤其在冗余操作期间),然后有秩序地中止任务。
以下是关系到线程属性的几个方法:
1)getId
用来得到线程ID
2)getName和setName
用来得到或者设置线程名称。
3)getPriority和setPriority
用来获取和设置线程优先级。
4)setDaemon和isDaemon
用来设置线程是否成为守护线程和判断线程是否是守护线程。
Thread类有一个比较常用的静态方法currentThread()用来获取当前线程。
参考如下:
http://www.cnblogs.com/dolphin0520/p/3920357.html
http://blog.csdn.net/lonelyroamer/article/details/7949969
http://www.open-open.com/lib/view/open1371741636171.html