浅谈Thread和Runnable
本文介绍Thread和Runnable创建线程的区别:
-两种创建线程方法的比较
-线程的生命周期
-线程的守护神-守护线程
- 总结
- 守护线程的代码
线程创建的两种方式比较:
- 继承Thread类创建多线程:
public class MyThread extends Thread{
@Override
public void run(){
...
}
}
MyThread t = new MyThread();
t.start();
- 实现Runnable接口:
class MyThread implements Runnable{
@Override
public void run(){
....
}
}
MyThread mt = new MyThread ();
Thread t = new Thread(mt);
t.start();
- 两种方式的比较:实现Runnable接口方式可以避免继承Thread方式由于Java单继承特性带来的缺陷。实现Runnable的代码可以被多个线程共享,适合于多个线程处理同一资源的情况.(具体见代码)
线程的生命周期:
- 线程的生命周期中有几种状态,如下图:
- 创建状态:新建一个线程对象。如:Thread td = new Thread();
- 就绪状态:创建了线程对象后,调用线程的start()方法(注意:此时线程只是进入了线程队列,等待获取CPU服务,具备了运行的条件,但并不一定已经开始运行了。)
- 运行状态:处于就绪状态的线程,一旦获取了CPU资源,便进入到运行状态,开始执行run方法的逻辑。
- 终止状态:线程的run方法执行完毕,或者线程调用了stop方法,线程进入终止状态。
阻塞状态:一个正在执行的线程在某些情况下,由于某种原因而暂时让出了CPU资源,暂停了自己的执行,便进入了阻塞状态,如调用了sleep方法。
线程的守护神
Java线程有两类:用户线程和守护线程。用户线程:运行在前台,执行具体的任务。守护线程:运行在后台,为前台线程服务。特点:一旦所有用户线程都结束运行,守护线程会随JVM一起结束工作。应用:数据库连接池中的监测线程、JVM虚拟机启动后的监测线程、垃圾回收线程。
- 如何设置守护线程:可以通过Thread类的setDaemon(true)方法来设置当前线程为守护线程。注意事项:setDaemon(true)方法必须在start()方法前调用,否则会抛出IllegalThreadStateException异常。
- 在守护线程中产生的线程也是守护线程。
- 不是所有的操作都能分配给守护线程来执行,比如读写操作或者计算逻辑。理解:假如读写操作是守护线程,当读写操作执行到一半,所有的用户线程都结束了,守护线程会随着JVM一起结束工作。守护线程执行的读写操作还没完成,程序有异常。后面有程序讲解
- 使用jstack生成线程快照:生成当前时刻线程的快照,即当前进程中所有线程的信息。帮助定位程序出现的原因。
jstack的使用:具体如下图
第二个参数pid对应的就是进程的进程id。使用如下图:
总结:
首先,本文介绍了Thread和Runnable创建线程的区别。
- 介绍了线程的生命周期和周期对应的代码和状态。
再次介绍了守护线程的概念和jstack工具。
守护线程的代码:
/** *创建一个守护线程,实现每一秒钟向文件写入一句话 * */ public class GuardThread implements Runnable{ @Override public void run(){ System.out.println("守护线程:"+Thread.currentThread().getName()+"开始执行"); wirterFile(); System.out.println("守护线程:"+Thread.currentThread().getName()+"结束执行"); } /** * 每一秒钟向文件中写入一句话 * @throws Exception */ private void wirterFile(){ OutputStream fileOutPutStream = null; try { File daemonFile = new File("D:"+File.separator+"daemon.txt"); //第二个参数表示已追加的形式来写文件 fileOutPutStream = new FileOutputStream(daemonFile,true); int count = 0; while(count<1000){ fileOutPutStream.write(("word "+count+"\r\n").getBytes()); System.out.println("守护线程:"+Thread.currentThread().getName()+"写入 word "+count++); /** * 每写一次文件休眠一秒 */ Thread.sleep(1000); } }catch(Exception e){ e.printStackTrace(); }finally { if(fileOutPutStream!=null){ try { fileOutPutStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
/*
*主线程类
*/
public class GuardThreadTest {
public static void main(String[] args) {
System.out.println("主线程"+Thread.currentThread().getName()+"开始执行");
GuardThread guardThread = new GuardThread();
Thread td = new Thread(guardThread);
//设置线程为守护线程
td.setDaemon(true);
//启动线程
td.start();
Scanner scanner = new Scanner(System.in);
//主线程阻塞 等待键盘输入
String keyWord = scanner.next();
System.out.println("主线程"+Thread.currentThread().getName()+"结束执行,键盘上输入的字:"+keyWord);
}
}