java thread梳理
2016-04-07 22:55
429 查看
之前的文章介绍了java线程的背景由来,还有创建线程的方法。本篇把线程的常用方法梳理一下,作为补充。
thread的常用方法里面,start方法与run方法之前介绍过。
如果我们需要让当前正在执行的线程暂停一段时间,并进入阻塞状态,则可以通过调用Thread的sleep方法,从上面可以看到sleep方法有两种重载的形式,但是使用方法一样。
sleep(long millis) //参数为毫秒
sleep(long millis,int nanoseconds) //第一参数为毫秒,第二个参数为纳秒
注意点:
1)sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象
从上面输出结果可以看出,当Thread-0进入睡眠状态之后,Thread-1并没有去执行具体的任务。只有当Thread-0执行完之后,此时Thread-0释放了对象锁,Thread-1才开始执行。
注意,如果调用了sleep方法,必须捕获InterruptedException异常或者将该异常向上层抛出。当线程睡眠时间满后,不一定会立即得到执行,因为此时可能CPU正在执行其他的任务。所以说调用sleep方法相当于让线程进入阻塞状态。
注意点2:sleep是Thread的静态类方法,谁调用的谁去睡觉,即使在a线程里调用了b的sleep方法,实际上还是a去睡觉,要让b线程睡觉要在b的代码中调用sleep。
运行结果:
3)sleep与调度准确性
因为使用sleep方法之后,线程是进入阻塞状态的,只有当睡眠的时间结束,才会重新进入到就绪状态,而就绪状态进入到运行状态,是由系统控制的,我们不可能精准的去干涉它,所以如果调用Thread.sleep(1000)使得线程睡眠1秒,可能结果会大于1秒。可以自己试试,就不写例子了。
关于sleep()方法和yield()方的区别如下:
①、sleep方法暂停当前线程后,会进入阻塞状态,只有当睡眠时间到了,才会转入就绪状态。而yield方法调用后 ,是直接进入就绪状态,所以有可能刚进入就绪状态,又被调度到运行状态。
②、sleep方法声明抛出了InterruptedException,所以调用sleep方法的时候要捕获该异常,或者显示声明抛出该异常。而yield方法则没有声明抛出任务异常。
③、sleep方法比yield方法有更好的可移植性,通常不要依靠yield方法来控制并发线程的执行。
3join方法
关于join的含义,有人说是“将几个并行线程的线程合并为一个单线程执行”,我个人觉得“等待该线程终止”更合适,也就是在线程调用了join()方法后面的代码,只有等到线程结束了才能执行。
join方法有三个重载版本:
可以看出,当调用thread.join()方法后,main线程会进入等待,然后等待thread执行完之后再继续执行。
实际上调用join方法是调用了Object的wait方法,这个可以通过查看源码得知:
wait方法会让线程进入阻塞状态,并且会释放线程占有的锁,并交出CPU执行权限。这里除了wait方法之外isAlive方法比较重要,就是判断当前线程的状态,也就是调用thread.join()的thread。
我们看看sleep、wait、join方法的声明:
[java] view
plain copy
public final void wait() throws InterruptedException
[java] view
plain copy
public static native void sleep(long millis) throws InterruptedException
[java] view
plain copy
public final void join() throws InterruptedException
这三者有一个共同点,都抛出了一个InterruptedException的异常。
每个Thread都有一个中断状状态,默认为false。可以通过Thread对象的isInterrupted()方法来判断该线程的中断状态。可以通过Thread对象的interrupt()方法将中断状态设置为true。当一个线程处于sleep、wait、join这三种状态之一的时候,如果此时他的中断状态为true,那么它就会抛出一个InterruptedException的异常,并将中断状态重新设置为false。
进入睡眠状态
睡眠完毕
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
thread的常用方法里面,start方法与run方法之前介绍过。
线程睡眠——sleep
如果我们需要让当前正在执行的线程暂停一段时间,并进入阻塞状态,则可以通过调用Thread的sleep方法,从上面可以看到sleep方法有两种重载的形式,但是使用方法一样。sleep(long millis) //参数为毫秒
sleep(long millis,int nanoseconds) //第一参数为毫秒,第二个参数为纳秒
注意点:
1)sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象
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方法
调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。但是yield不能控制具体的交出CPU的时间,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。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方法有三个重载版本:
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>}运行结果:
可以看出,当调用thread.join()方法后,main线程会进入等待,然后等待thread执行完之后再继续执行。
实际上调用join方法是调用了Object的wait方法,这个可以通过查看源码得知:
wait方法会让线程进入阻塞状态,并且会释放线程占有的锁,并交出CPU执行权限。这里除了wait方法之外isAlive方法比较重要,就是判断当前线程的状态,也就是调用thread.join()的thread。
4.interrupt方法
上面文章说过如何中断一个线程,就是单独调用interrupt方法可以使得处于阻塞状态的线程抛出一个异常,也就说,它可以用来中断一个正处于阻塞状态的线程;另外,通过interrupt方法和isInterrupted()方法来停止正在运行的线程。我们看看sleep、wait、join方法的声明:
[java] view
plain copy
public final void wait() throws InterruptedException
[java] view
plain copy
public static native void sleep(long millis) throws InterruptedException
[java] view
plain copy
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
相关文章推荐
- Spring4学习(三):注解配置bean
- 【通信15】JAVA OOP授课计划
- moon struts 2
- 几个主流的Java连接池整理
- Spring注解
- java的四种引用类型:强、弱、软、虚
- 吴超老师课程--HBASE的Java_API
- JAVA模块化
- eclipse连接mysql驱动错误
- Java中如何遍历Map对象的4种方法
- springmvc(3)
- Myeclipse添加反编译JadClipse插件
- Java内存泄露
- 新学JAVA二三事之字符串输出
- java工程中使用freemarker例子
- java 基础-
- Java调用动态链接库
- 浅识——java类的继承
- 非XA式Spring分布式事务
- java命令执行jar包的方式