关于线程基础

线程的生命周期

这里写图片描述

创建:新建一个线程对象,new Thread = new Thread()

就绪:创建了线程对象后,调用了start()方法(注意线程此时只是进入了线程队列,等待获取CPU服务,具备了运行条件,但不代表可以马上运行)

运行:处于就绪状态的线程,一旦获取了CPU服务,便进入了运行状态,开始执行run()方法里面的逻辑

终止:当线程run()方法执行完毕,或者线程调用了stop()方法,那么线程就会终止

阻塞:一个正在执行的线程在某些情况下,由于某种原因暂时让出了CPU资源,暂停了自己的执行,便进入了阻塞状态,如调用了sleep()方法

线程状态。 线程可以处于以下状态之一:
NEW :尚未启动的线程处于此状态。 1
RUNNABLE :在Java虚拟机中执行的线程处于此状态。 2
BLOCKED :被阻塞等待监视器锁定的线程处于此状态。 3
WAITING :正在等待另一个线程执行特定动作的线程处于此状态。 4
TIMED_WAITING :正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。 5
TERMINATED :已退出的线程处于此状态。 6

其中:

New对应的是创建
RUNNABLE对应的是就绪状态&运行状态
BLOCKED、WAITING、TIMED_WAITING对应的是阻塞状态
TERMINATED对应的是终止状态

守护线程

java线程有两类

1、用户线程:运行在前台,执行具体任务

程序的主线程、连接网络的子线程等都是用户线程
(这类线程我们看的见他们在工作,但是摸不着)

2、守护线程:运行在后台,为其他前台线程服务

特点:一旦所有的用户线程都结束运行,那么守护线程会觉得自己没有存在的必要了,就会随着JVM一起停止工作
应用:数据库连接池中的监控线程(如连接池中连接的个数、连接超时的时间等)
…………JVM虚拟机启动后的监控线程(如检测内存使用的情况等),还有最常见的就是:垃圾回收线程!

3、如何设置守护线程

可以通过调用Tread类的setDaemon(true)方法来把当前的线程设置为守护线程

注意事项:
1、setDaemon(true)的这个方法必须在start()方法前调用,不然会抛出illgalThreadstateException异常(首字母应大写,这里是博客的字体,i的大写与l傻傻分不清楚)
2、在守护线程中产生的新线程也是守护线程
3、不是所有的任务都可以分配给守护线程来执行,如读写操作或者计算逻辑(如果守护线程执行读写操作或者计算逻辑,如果用户线程都结束了,那么守护线程也停止了,那么程序执行不完就崩溃了)

代码演示

场景:守护线程向文件中不断的输出字符串,当主线程阻塞停止时,守护线程退出关闭
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.Scanner;

class DemonThread implements Runnable{
//用Runnable是为了提高程序的可扩展性
    @Override
    public void run() {
        System.out.println("进入守护线程:"+Thread.currentThread().getName());
        try {
            writeToFile();
        } catch (Exception e) {
            e.printStackTrace();
        }
        //如果守护线程结束,那么就打印退出守护线程
        System.out.println("退出守护线程:"+Thread.currentThread().getName());
    }
    public void writeToFile() throws Exception{//这里是为了方便把所有的异常全给抛出去了
        File file = new File("d:"+File.separator+"demo.text");//中间的相当于路径的斜线/
        OutputStream os = new FileOutputStream(file,true);
        int count = 0;
        while(count<999){
            os.write(("\r\nword:"+count).getBytes());
            System.out.println("守护线程"+Thread.currentThread().getName()
                    +"向文件中写入了"+count);
            count++;
            Thread.sleep(1000); 
        }
    }
}
public class DemonThreadDemo {

    public static void main(String[] args) {
        System.out.println("进入主线程"+Thread.currentThread().getName());
        DemonThread dt = new DemonThread();
        Thread thread = new Thread(dt);
        thread.setDaemon(true);
        thread.start();
        //主线程要干的操作,键盘输入导致线程阻塞
        Scanner sc = new Scanner(System.in);
        sc.next();
        System.out.println("退出主线程"+Thread.currentThread().getName());
    }
}

结果为:
控制台
这里写图片描述
d:/demo.text
这里写图片描述


使用jstack生成线程快照

//在jdk的文件夹 lib下,有jstack.exe(命令行工具)还有jvisualvm.exe(界面化工具)

jstack

作用:生成JVM当前时刻线程的快照(threaddump,即当前进程中的所有的线程信息)。

目的:帮助定位程序问题出现的原因,如长时间停顿、CPU占用率过高等。

这里写图片描述

常用参数:(以下参数可有可无)

-F jstack [-l] pid无法响应时,强制打印堆栈

-l l长列表. 打印关于锁的附加信息,例如属于java.util.concurrent的ownable synchronizers列表.

-m 混合模式输出(包括java和本地c/c++片段)堆栈。

pid: java应用程序的进程号,可以通过来任务管理器获得;
executable:产生core dump的java可执行程序;
core:打印出的core文件;
remote-hostname-or-ip:远程debug服务器的名称或IP;
server-id: 唯一id,假如一台主机上多个远程debug服务;
这里写图片描述


补充:

为了保证程序的可扩展性,建议使用实现Runnable接口的这种方式创建线程
1.程序中的同一资源指的是同一个Runnable对象,,,如果把其他类的对象也作为共享对象,那么继承Tread类和实现Runnable接口,是都可以处理同一资源的

2.安全的卖票程序中需要加入同步(Synchronized)

发布了25 篇原创文章 · 获赞 12 · 访问量 3万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章