转载请标明出处:
http://blog.csdn.net/hai_qing_xu_kong/article/details/70339618
本文出自:【顾林海的博客】
前言
在上一篇文章《有关线程的相关知识(1)》 一文中已经讲了线程的两种创建方式以及6个状态,这篇文章就把接下来的东西加上,各位看官,提好裤子,上车咯。
线程的中断
在java中,当所有非守护进程运行结束或是其中一个线程调用了System.exit()方法时java程序就运行结束,这里面的守护线程是一种特殊的线程,它是系统的守护者,在后台默默地完成一些系统性的服务,比如垃圾回收线程、JIT线程(动态编译器)就可以理解为守护线程,如果守护线程要守护的对象已经不存在了,那么整个应用程序就会结束,也就是说,在Java应用中如果只剩下守护线程时,Java虚拟机就会自然退出。
我们来编写这样一个程序,创建一个线程,在这个线程中一直打印信息,接着我们给这个线程设置为守护线程,并在主线程中休眠2秒后,看看创建的线程是否继续打印:
public class Daemon implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
System.out.println("hello....");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class Client {
public static void main(String[] args) {
Thread daemonThread=new Thread(new Daemon());
daemonThread.setDaemon(true);
daemonThread.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
输出的结果:
hello….
hello….
hello….
可以发现将daemonThread设置为守护线程,系统中只有主线程main为用户线程,在main线程休眠2秒后退出,整个程序也结束,如果不把daemonThread设置为守护线程,main线程结束后,daemonThread线程会不停的打印,永远不会结束。
在Java中提供了中断机制,可以使用它来结束一个线程,这种机制要求线程检查它是否被中断,然后决定是不是响应这个中断请求,并且线程是可以忽略这个中断请求。
接下来创建一个线程并不停打印累加的number值,在打印前先判断当前线程是否被中断,如果被中断,就打印信息,接着退出。
public class MyThread extends Thread{
private int number=0;
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
if(isInterrupted()){
System.out.println("interrupted...");
return;
}
System.out.println("number="+number);
number++;
}
}
}
public class Client {
public static void main(String[] args) {
MyThread myThread=new MyThread();
myThread.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
myThread.interrupt();
}
}
输出:
…………………..
number=465110
number=465111
number=465112
interrupted…
可以看出当myThread线程被中断后,程序就运行终止,Thread类有一个表明线程是否被中断的属性,类型是布尔值,当线程的interrupt()方法被调用时,这个属性就会被赋值为true。
isInterrupted()会返回上面提到的属性值,除了使用isInterrupted()方法检查线程是否被中断,还有一个方法interrupted()也是检查线程的中断与否,两种方法的区别在与:isInterrupted()不能改变interrupted属性的值,但interrupted()方法能设置interrupted 属性为false。
线程的休眠与恢复
线程提供一个sleep()方法,表示休眠,在休眠的这段时间,线程不占用计算机的任何资源,sleep()方法接受整型数值作为参数,来表明线程挂起执行的毫秒数。sleep()方法的另一种使用方式是通过 TimeUnit枚举类元素进行调用,它接受的参数单位是秒,最后会被转化成毫秒。
在接下来的程序中,通过TimeUnit类的SECONDS属性的sleep()方法来挂起一秒钟,之后输出信息。
public class MyThread extends Thread{
private int number=0;
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
System.out.println("number="+number);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
System.out.println("InterruptedException...");
}
number++;
}
}
}
public class Client {
public static void main(String[] args) {
MyThread myThread=new MyThread();
myThread.start();
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
myThread.interrupt();
}
}
在主类中,我们运行这个线程并在挂起一段时间后中断这个线程,我们在myThread线程中的调用sleep()方法时捕获InterruptedException异常时输出一段信息(推荐当线程被中断时,可以在捕获这个异常时进行释放或者关闭线程正在使用的资源)。
输出:
number=2
number=3
number=4
InterruptedException…
number=5
number=6
number=7
number=8
…………….
…………….
除了sleep()方法外,Java并发API还提供了另一个方法来使线程对象释放CPU,这个方法是yield()方法,它会通知JVM这个线程对象可以释放CPU了,但JVM并不保证遵循这个要求。
等待线程的终止
有时候,我们需要等待线程的终止,比如初始化资源,只有在初始化完毕后才能继续执行下去,达到这个目的,我们可以使用Thread类的join()方法,当一个线程对象调用join()方法时,调用它当线程将被挂起,直到这个线程对象完成它当任务。
我们创建两个个线程,其中每隔一秒计数,当第一个线程到达5并且第二线程到达6时,主线程才能继续执行下去。
public class MyThread1 extends Thread{
private int number=0;
@Override
public void run() {
// TODO Auto-generated method stub
while(number<=5){
System.out.println("number="+number);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
System.out.println("InterruptedException...");
}
number++;
}
}
}
public class MyThread2 extends Thread{
private int number=0;
@Override
public void run() {
// TODO Auto-generated method stub
while(number<=6){
System.out.println("number="+number);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
System.out.println("InterruptedException...");
}
number++;
}
}
}
public class Client {
public static void main(String[] args) {
MyThread1 myThread1=new MyThread1();
MyThread2 myThread2=new MyThread2();
myThread1.start();
myThread2.start();
try {
myThread1.join();
myThread2.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("终于轮到我了.....");
}
}
输出:
number=0
number=0
number=1
number=1
number=2
number=2
number=3
number=3
number=4
number=4
number=5
number=5
number=6
终于轮到我了…..
当myThread1与myThread2都结束时,主线程对象才会继续执行下去。
线程的分组
Java并发API提供了一个功能,能够把线程分组,这样的话,一组线程可以当作一个单一的单元 , Java提供了ThreadGroup类表示一组线程,线程组可以包含线程对象,也可以包含其他的线程组对象,它是一个树形结构。
public class ThreadGroupClient implements Runnable{
@Override
public void run() {
while(true){
System.out.println("group:"+Thread.currentThread().getThreadGroup().getName()+"----thread:"+Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ThreadGroup threadGroup=new ThreadGroup("group");
Thread thread1=new Thread(new ThreadGroupClient(),"thread1");
Thread thread2=new Thread(new ThreadGroupClient(),"thread2");
thread1.start();
thread2.start();
System.out.println(threadGroup.activeCount());
threadGroup.list();
}
}
输出:
0
group:main—-thread:thread2
group:main—-thread:thread1
java.lang.ThreadGroup[name=group,maxpri=10]
group:main—-thread:thread1
group:main—-thread:thread2
group:main—-thread:thread1
group:main—-thread:thread2
创建一个名叫group的线程组,并将thread1和thread2加入这个线程组中,activeCount()可以获得活动线程的总数,但由于线程是动态的,因此这个值只是一个估计值,无法确定精确,list()方法打印这个线程组中所有的线程信息。