您的位置:首页 > 编程语言 > Java开发

java thread梳理

2016-04-07 22:55 429 查看
之前的文章介绍了java线程的背景由来,还有创建线程的方法。本篇把线程的常用方法梳理一下,作为补充。

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: