Properties属性文件,XML文件,日志,多线程

目录

  • 一、Properties属性文件
  • 二、XML文件
  • 三、日志
  • 四、多线程
    • 1.线程
    • 2.线程的实现方法(3种)
    • 3.Thread的常用方法
    • 4.线程同步-----解决线程安全问题
    • 5.线程通信
    • 6.线程池
    • 7.并发和并行
    • 8.线程的生命周期


一、Properties属性文件

  1. 属性文件后缀以.properties结尾
  2. 属性文件里面的每一行都是一个键值对,键和值中间用=隔开。比如: admin=123456
  3. #表示这样是注释信息,是用来解释这一行配置是什么意思。
  4. 每一行末尾不要习惯性加分号,以及空格等字符;不然会把分号,空格会当做值的一部分。
  5. 键不能重复,值可以重复

Properties是什么
Properties是Map接口下面的一个实现类,所以Properties也是一种双列集合,用来存储键值对。 但是一般不会把它当做集合来使用。

Properties核心作用
Properties类的对象,用来表示属性文件,可以用来读取属性文件中的键值对。

使用Properties读取属性文件里的键值对数据

构造器说明
public Properties()用于构建Properties集合对象(空容器)
常用方法说明
public void load(InputStream is)通过字节输入流,读取属性文件里的键值对数据
public void load(Reader reader)通过字符输入流,读取属性文件里的键值对数据
public String getProperty(String key)根据键获取值(其实就是get方法的效果)
public Set stringPropertyNames()获取全部键的集合(其实就是ketSet方法的效果)

实用Properties读取属性文件的步骤如下

1、创建一个Properties的对象出来(键值对集合,空容器)
2、调用load(字符输入流/字节输入流)方法,开始加载属性文件中的键值对数据到properties对象中去
3、调用getProperty(键)方法,根据键取值

代码如下:

public class PropertiesTest1 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个Properties的对象出来(键值对集合,空容器)
        Properties properties = new Properties();
        System.out.println(properties);

        // 2、开始加载属性文件中的键值对数据到properties对象中去
        properties.load(new FileReader("properties-xml-log-app\\src\\users.properties"));
        System.out.println(properties);

        // 3、根据键取值
        System.out.println(properties.getProperty("张三"));
        System.out.println(properties.getProperty("李四"));

        // 4、遍历全部的键和值。
        //获取键的集合
        Set<String> keys = properties.stringPropertyNames();
        for (String key : keys) {
            //再根据键获取值
            String value = properties.getProperty(key);
            System.out.println(key + "---->" + value);
        }
		
        properties.forEach((k, v) -> {
            System.out.println(k + "---->" + v);
        });
    }
}

使用Properties把键值对数据写出到属性文件里去

构造器说明
public Properties()用于构建Properties集合对象(空容器)
常用方法说明
public Object setProperty(String key, String value)保存键值对数据到Properties对象中去。
public void store(OutputStream os, String comments)把键值对数据,通过字节输出流写出到属性文件里去
public void store(Writer w, String comments)把键值对数据,通过字符输出流写出到属性文件里去

往Properties属性文件中写键值对的步骤如下

1、先准备一个.properties属性文件,按照格式写几个键值对
2、创建Properties对象出来,
3、调用setProperty存储一些键值对数据
4、调用store(字符输出流/字节输出流, 注释),将Properties集合中的键和值写到文件中
注意:第二个参数是注释,必须得加;

二、XML文件

什么是XML

XML是可扩展的标记语言,意思是它是由一些标签组成 的,而这些标签是自己定义的。本质上一种数据格式,可以用来表示复杂的数据关系。

XML文件有如下的特点:

  • XML中的<标签名> 称为一个标签或者一个元素,一般是成对出现的。
  • XML中的标签名可以自己定义(可扩展),但是必须要正确的嵌套
  • XML中只能有一个根标签。
  • XML标准中可以有属性
  • XML必须第一行有一个文档声明,格式是固定的<?xml version="1.0" encoding="UTF-8"?>
  • XML文件必须是以.xml为后缀结尾

XML的创建

就是创建一个XML类型的文件,要求文件的后缀必须使用xml,如hello.xml

XML的语法规则

XML文件的后缀名为:xml,文档声明必须是第一行

<? xml version="1.0" encoding="UTF-8" ?>
 version:XML默认的版本号码、该属性是必须存在的
 encoding:本XML文件的编码

XML中可以定义注释信息:

XML中书写”<”、“&”等,可能会出现冲突,导致报错,此时可以用如下特殊字符替代。

&lt;  表示 <
&gt;  表示 >
&amp; 表示 &
&apos; 表示 '
&quot; 表示 "

XML中可以写一个叫CDATA的数据区:<![CDATA[ …内容… ]]>,里面的内容可以随便写。

XML的作用和应用场景

本质是一种数据格式,可以存储复杂的数据结构,和数据关系。

应用场景:经常用来做为系统的配置文件;或者作为一种特殊的数据结构,在网络中进行传输。

使用Dom4J解析出XML文件

分析:
①下载Dom4j框架,官网下载。
②在项目中创建一个文件夹:lib
③将dom4j-2.1.3.jar文件复制到 lib 文件夹
④在jar文件上点右键,选择 Add as Library -> 点击OK
⑤在类中导包使用

Dom4j解析XML得到Document对象

SAXReader:Dom4j提供的解析器,可以认为是代表整个Dom4j框架

构造器/方法说明
public SAXReader()构建Dom4J的解析器对象
public Document read(String url)把XML文件读成Document对象
public Document read(InputStream is)通过字节输入流读取XML文件

Document

方法名说明
Element getRootElement()获得根元素对象

XML解析的过程,是从根元素开始,从外层往里层解析。 先把Document对象,和根元素获取出来

public class Dom4JTest1 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个Dom4J框架提供的解析器对象
        SAXReader saxReader = new SAXReader();

        // 2、使用saxReader对象把需要解析的XML文件读成一个Document对象。
        Document document =
                saxReader.read("properties-xml-log-app\\src\\helloworld.xml");

        // 3、从文档对象中解析XML文件的全部数据了
        Element root = document.getRootElement();
        System.out.println(root.getName());
    }
}

Element提供的方法

方法名说明
public String getName()得到元素名字
public List elements()得到当前元素下所有子元素
public List elements(String name)得到当前元素下指定名字的子元素返回集合
public Element element(String name)得到当前元素下指定名字的子元素,如果有很多名字相同的返回第一个
public String attributeValue(String name)通过属性名直接得到属性值
public String elementText(子元素名)得到指定名称的子元素的文本
public String getText()得到文本

三、日志

用来记录程序运行过程中的信息,并可以进行永久存储。

日志可解决的问题

  • 系统系统能记住某些数据被谁操作,比如被谁删除了?
  • 想分析用户浏览系统的具体情况,比如挖掘用户的具体喜好?
  • 当系统在开发中或者上线后出现了Bug,崩溃了,该通过什么去分析,定位Bug?

日志技术有如下好处

  • 日志可以将系统执行的信息,方便的记录到指定位置,可以是控制台、可以是文件、可以是数据库中。
  • 日志可以随时以开关的形式控制启停,无需侵入到源代码中去修改。

日志技术的特点和优势

输出语句日志技术
输出位置输出到控制台可以将日志信息写入到文件或者数据库中
取消日志需要修改代码,灵活性比较差不需要修改代码,灵活性比较好
多线程性能较差性能较好

日志技术的体系结构
在这里插入图片描述
Logback的三个模块

  • slf4j-api:日志接口
  • logback-core:基础模块
  • logback-classic:功能模块,它完整实现了slf4j API

日志级别指的是日志信息的类型,日志都会分级别,常见的日志级别如下(优先级依次升高)

日志级别说明
trace追踪,指明程序运行轨迹
debug调试,实际应用中一般将其作为最低级别,而 trace 则很少使用
info输出重要的运行信息,数据连接、网络连接、IO操作等等,使用较多
warn警告信息,可能会发生问题,使用较多
error错误信息, 使用较多

Logback提供了一个核心配置文件logback.xml

<root level=“info">
   <appender-ref ref="CONSOLE"/>
   <appender-ref ref="FILE" />
 </root>

只有日志的级别是大于或等于核心配置文件配置的日志级别,才会被记录,否则不记录。

四、多线程

1.线程

什么是线程
线程(Thread)是一个程序内部的一条执行流程。
程序中如果只有一条执行流程,那这个程序就是单线程的程序。

多线程是什么
多线程是指从软硬件上实现的多条执行流程的技术(多条线程由CPU负责调度执行)

2.线程的实现方法(3种)

多线程的创建方式一:继承Thread类
步骤
①定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法
②创建MyThread类的对象
③调用线程对象的start()方法启动线程(启动后还是执行run方法的)

代码如下

public class MyThread extends Thread{
    // 2、必须重写Thread类的run方法
    @Override
    public void run() {
        // 描述线程的执行任务。
        for (int i = 1; i <= 5; i++) {
            System.out.println("子线程MyThread输出:" + i);
        }
    }
}

再定义一个测试类,在测试类中创建MyThread线程对象,并启动线程

public class ThreadTest1 {
    // main方法是由一条默认的主线程负责执行。
    public static void main(String[] args) {
        // 3、创建MyThread线程类的对象代表一个线程
        Thread t = new MyThread();
        // 4、启动线程(自动执行run方法的)
        t.start(); 

        for (int i = 1; i <= 5; i++) {
            System.out.println("主线程main输出:" + i);
        }
    }
}

方式一优缺点:
优点:编码简单
缺点:线程类已经继承Thread,无法继承其他类,不利于功能的扩展。

多线程的注意事项
①启动线程必须是调用start方法,不是调用run方法。
②不要把主线程任务放在启动子线程之前。

  • 直接调用run方法会当成普通方法执行,此时相当于还是单线程执行。
  • 只有调用**start方法才是启动一个新的线程执行。
  • 这样主线程一直是先跑完的,相当于是一个单线程的效果了。

多线程的创建方式二:实现Runnable接口

①定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法
②创建MyRunnable任务对象
③把MyRunnable任务对象交给Thread处理。
④调用线程对象的start()方法启动线程

Thread类提供的构造器说明
public Thread(Runnable target)封装Runnable对象成为线程对象

先准备一个Runnable接口的实现类

/**
 * 1、定义一个任务类,实现Runnable接口
 */
public class MyRunnable implements Runnable{
    // 2、重写runnable的run方法
    @Override
    public void run() {
        // 线程要执行的任务。
        for (int i = 1; i <= 5; i++) {
            System.out.println("子线程输出 ===》" + i);
        }
    }
}

在测试类中创建线程对象,并执行线程

public class ThreadTest2 {
    public static void main(String[] args) {
        // 3、创建任务对象。
        Runnable target = new MyRunnable();
        // 4、把任务对象交给一个线程对象处理。
        //  public Thread(Runnable target)
        new Thread(target).start();

        for (int i = 1; i <= 5; i++) {
            System.out.println("主线程main输出 ===》" + i);
        }
    }
}

方式二的优缺点

优点:任务类只是实现接口,可以继续继承其他类、实现其他接口,扩展性强。
缺点:需要多一个Runnable对象。

线程创建方式二的匿名内部类写法

①可以创建Runnable的匿名内部类对象。
②再交给Thread线程对象。
③再调用线程对象的start()启动线程。

直接创建Runnable接口的匿名内部类对象,传递给Thread对象。

public class ThreadTest2_2 {
    public static void main(String[] args) {
        // 1、直接创建Runnable接口的匿名内部类形式(任务对象)
        Runnable target = new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 5; i++) {
                    System.out.println("子线程1输出:" + i);
                }
            }
        };
        new Thread(target).start();

        // 简化形式1:
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 5; i++) {
                    System.out.println("子线程2输出:" + i);
                }
            }
        }).start();

        // 简化形式2:
        new Thread(() -> {
                for (int i = 1; i <= 5; i++) {
                    System.out.println("子线程3输出:" + i);
                }
        }).start();

        for (int i = 1; i <= 5; i++) {
            System.out.println("主线程main输出:" + i);
        }
    }
}

多线程的方式三:利用Callable接口、FutureTask类来实现。

①、创建任务对象
定义一个类实现Callable接口,重写call方法,封装要做的事情,和要返回的数据。
把Callable类型的对象封装成FutureTask(线程任务对象)。
②、把线程任务对象交给Thread对象。
③、调用Thread对象的start方法启动线程。
④、线程执行完毕后、通过FutureTask对象的的get方法去获取线程任务执行的结果。

FutureTask的API

FutureTask提供的构造器说明
public FutureTask<>(Callable call)把Callable对象封装成FutureTask对象。
FutureTask提供的方法说明
public V get() throws Exception获取线程执行call方法返回的结果。

先准备一个Callable接口的实现类

/**
 * 1、让子类继承Thread线程类。
 */
public class MyThread extends Thread{
    // 2、必须重写Thread类的run方法
    @Override
    public void run() {
        // 描述线程的执行任务。
        for (int i = 1; i <= 5; i++) {
            System.out.println("子线程MyThread输出:" + i);
        }
    }
}

在测试类中创建线程并启动线程,还要获取返回结果

public class ThreadTest3 {
    public static void main(String[] args) throws Exception {
        // 3、创建一个Callable的对象
        Callable<String> call = new MyCallable(100);
        // 4、把Callable的对象封装成一个FutureTask对象(任务对象)
        // 未来任务对象的作用?
        // 1、是一个任务对象,实现了Runnable对象.
        // 2、可以在线程执行完毕之后,用未来任务对象调用get方法获取线程执行完毕后的结果。
        FutureTask<String> f1  = new FutureTask<>(call);
        // 5、把任务对象交给一个Thread对象
        new Thread(f1).start();
        
        Callable<String> call2 = new MyCallable(200);
        FutureTask<String> f2  = new FutureTask<>(call2);
        new Thread(f2).start();

        // 6、获取线程执行完毕后返回的结果。
        // 注意:如果执行到这儿,假如上面的线程还没有执行完毕
        // 这里的代码会暂停,等待上面线程执行完毕后才会获取结果。
        String rs = f1.get();
        System.out.println(rs);

        String rs2 = f2.get();
        System.out.println(rs2);
    }
}

线程创建方式三的优缺点

优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强;可以在线程执行完毕后去获取线程执行的结果。
缺点:编码复杂一点。

三种线程的创建方式,和不同点

方式优点缺点
继承Thread类编程比较简单,可以直接使用Thread类中的方法扩展性较差,不能再继承其他的类,不能返回线程执行的结果
实现Runnable接口扩展性强,实现该接口的同时还可以继承其他的类。编程相对复杂,不能返回线程执行的结果
实现Callable接口扩展性强,实现该接口的同时还可以继承其他的类。可以得到线程执行的结果编程相对复杂

3.Thread的常用方法

Thread****提供的常用方法说明
public void run()线程的任务方法
public void start()启动线程
public String getName**()**获取当前线程的名称,线程名称默认是Thread-索引
public void setName**(String name)**为线程设置名称
public static Thread currentThread**()**获取当前执行的线程对象
public static void sleep(long time)让当前执行的线程休眠多少毫秒后,再继续执行
public final void join()…让调用当前这个方法的线程先执行完!
Thread****提供的常见构造器说明
public Thread(String name)可以为当前线程指定名称
public Thread(Runnable target)封装Runnable对象成为线程对象
public Thread(Runnable target, String name)封装Runnable对象成为线程对象,并指定线程名称

4.线程同步-----解决线程安全问题

线程同步的思想
让多个线程实现先后依次访问共享资源,这样就解决了安全问题。

方式一:同步代码块
作用:把访问共享资源的核心代码给上锁,以此保证线程安全。

//锁对象:必须是一个唯一的对象(同一个地址)
synchronized(锁对象){
    //...访问共享数据的代码...
}
// 小明 小红线程同时过来的
public void drawMoney(double money) {
    // 先搞清楚是谁来取钱?
    String name = Thread.currentThread().getName();
    // 1、判断余额是否足够
    // this正好代表共享资源!
    synchronized (this) {
        if(this.money >= money){
            System.out.println(name + "来取钱" + money + "成功!");
            this.money -= money;
            System.out.println(name + "来取钱后,余额剩余:" + this.money);
        }else {
            System.out.println(name + "来取钱:余额不足~");
        }
    }
}

原理:每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程才可以进来执行

同步锁的注意事项

对于当前同时执行的线程来说,同步锁必须是同一把(同一个对象),否则会出bug。

锁对象的使用规范

建议使用共享资源作为锁对象,对于实例方法建议使用this作为锁对象。
对于静态方法建议使用字节码(类名.class)对象作为锁对象。

方式二:同步方法
作用:把访问共享资源的核心方法给上锁,以此保证线程安全。

修饰符 synchronized 返回值类型 方法名称(形参列表) {	
    操作共享资源的代码
}

把整个方法给锁住,一个线程调用这个方法,另一个线程调用的时候就执行不了,只有等上一个线程调用结束,下一个线程调用才能继续执行。

// 同步方法
public synchronized void drawMoney(double money) {
    // 先搞清楚是谁来取钱?
    String name = Thread.currentThread().getName();
    // 1、判断余额是否足够
    if(this.money >= money){
        System.out.println(name + "来取钱" + money + "成功!");
        this.money -= money;
        System.out.println(name + "来取钱后,余额剩余:" + this.money);
    }else {
        System.out.println(name + "来取钱:余额不足~");
    }
}

原理:每次只能一个线程进入,执行完毕以后自动解锁,其他线程才可以进来执行

同步方法底层原理

同步方法其实底层也是有隐式锁对象的,只是锁的范围是整个方法代码。
如果方法是实例方法:同步方法默认用this作为的锁对象。
如果方法是静态方法:同步方法默认用类名.class作为的锁对象。

方式三:Lock锁

Lock锁是JDK5开始提供的一个新的锁定操作,通过它可以创建出锁对象进行加锁和解锁,更灵活、更方便、更强大。
Lock是接口,不能直接实例化,可以采用它的实现类ReentrantLock来构建Lock锁对象。

构造器说明
public ReentrantLock()获得Lock锁的实现类对象

Lock的常用方法

方法名称说明
void lock()获得锁
void unlock()释放锁
1.首先在成员变量位子,需要创建一个Lock接口的实现类对象(这个对象就是锁对象)
	private final Lock lk = new ReentrantLock();
2.在需要上锁的地方加入下面的代码
	 lk.lock(); // 加锁
	 //...中间是被锁住的代码...
	 lk.unlock(); // 解锁

使用Lock锁改写前面DrawThread中取钱的方法

// 创建了一个锁对象
private final Lock lk = new ReentrantLock();

public void drawMoney(double money) {
        // 先搞清楚是谁来取钱?
        String name = Thread.currentThread().getName();
        try {
            lk.lock(); // 加锁
            // 1、判断余额是否足够
            if(this.money >= money){
                System.out.println(name + "来取钱" + money + "成功!");
                this.money -= money;
                System.out.println(name + "来取钱后,余额剩余:" + this.money);
            }else {
                System.out.println(name + "来取钱:余额不足~");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lk.unlock(); // 解锁
        }
    }
}

5.线程通信

什么是线程通信

当多个线程共同操作共享的资源时,线程间通过某种方式互相告知自己的状态,以相互协调,并避免无效的资源争夺。

线程通信的常见模型(生产者与消费者模型)
生产者线程负责生产数据
消费者线程负责消费生产者生产的数据。
注意:生产者生产完数据应该等待自己,通知消费者消费;消费者消费完数据也应该等待自己,再通知生产者生产!

Object类的等待和唤醒方法

方法名称说明
void wait()让当前线程等待并释放所占锁,直到另一个线程调用notify()方法或 notifyAll()方法
void notify()唤醒正在等待的单个线程
void notifyAll()唤醒正在等待的所有线程

注意

上述方法应该使用当前同步锁对象进行调用。

6.线程池

线程池就是一个可以复用线程的技术

谁代表线程池?
JDK 5.0起提供了代表线程池的接口:ExecutorService。

如何得到线程池对象

方式一:使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象。
方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象。

ThreadPoolExecutor****构造器

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
                          BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) 

参数一:corePoolSize : 指定线程池的核心线程的数量。 (正式工:3)
参数二:maximumPoolSize:指定线程池的最大线程数量。(最大员工数:5,临时工:2)
参数三:keepAliveTime :指定临时线程的存活时间。(临时工空闲多久被开除)
参数四:unit:指定临时线程存活的时间单位(秒、分、时、天)
参数五:workQueue:指定线程池的任务队列。(客人排队的地方)
参数六:threadFactory:指定线程池的线程工厂。(负责招聘员工的(hr))
参数七:handler:指定线程池的任务拒绝策略(线程都在忙,任务队列也满了的时候,新任务来了该怎么处理)

线程池的注意事项

临时线程什么时候创建
新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。
什么时候会开始拒绝新任务
核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始拒绝任务。

线程池处理Runnable任务
ExecutorService的常用方法

方法名称说明
void execute(Runnable command)执行 Runnable 任务
Future submit(Callable task)执行 Callable 任务,返回未来任务对象,用于获取线程返回的结果
void shutdown()等全部任务执行完毕后,再关闭线程池!
List shutdownNow()立刻关闭线程池,停止正在执行的任务,并返回队列中未执行的任务

新任务拒绝策略

策略详解
ThreadPoolExecutor.AbortPolicy丢弃任务并抛出RejectedExecutionException异常。是默认的策略
ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常 这是不推荐的做法
ThreadPoolExecutor.DiscardOldestPolicy抛弃队列中等待最久的任务 然后把当前任务加入队列中
ThreadPoolExecutor.CallerRunsPolicy由主线程负责调用任务的run()方法从而绕过线程池直接执行

线程池处理Callable任务
ExecutorService的常用方法

方法名称说明
void execute(Runnable command)执行任务/命令,没有返回值,一般用来执行 Runnable 任务
Future submit(Callable task)执行任务,返回未来任务对象获取线程结果,一般拿来执行 Callable 任务
void shutdown()等任务执行完毕后关闭线程池
List shutdownNow()立刻关闭,停止正在执行的任务,并返回队列中未执行的任务

Executors工具类实现线程池
Executors是一个线程池的工具类,提供了很多静态方法用于返回不同特点的线程池对象。

方法名称说明
public static ExecutorService newFixedThreadPool(int nThreads)创建固定线程数量的线程池,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程替代它。
public static ExecutorService newSingleThreadExecutor()创建只有一个线程的线程池对象,如果该线程出现异常而结束,那么线程池会补充一个新线程。
public static ExecutorService newCachedThreadPool()线程数量随着任务增加而增加,如果线程任务执行完毕且空闲了60s则会被回收掉。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)创建一个线程池,可以实现在给定的延迟后运行任务,或者定期执行任务。

注意 :这些方法的底层,都是通过线程池的实现类ThreadPoolExecutor创建的线程池对象。

7.并发和并行

  • 正常运行的程序(软件)就是一个独立的进程
  • 线程是属于进程,一个进程中包含多个线程
  • 进程中的线程其实并发和并行同时存在

并发的含义
进程中的线程是由CPU负责调度执行的,但CPU能同时处理线程的数量有限,为了保证全部线程都能往前执行,CPU会轮询为系统的每个线程服务,由于CPU切换的速度很快,给我们的感觉这些线程在同时执行,这就是并发。(并发就是多条线程交替执行)

并行的理解
在同一个时刻上,同时有多个线程在被CPU调度执行。

8.线程的生命周期

线程的生命周期
也就是线程从生到死的过程中,经历的各种状态及状态转换。
理解线程这些状态有利于提升并发编程的理解能力。

Java线程的状态
Java总共定义了6种状态
6种状态都定义在Thread类的内部枚举类中。

public class Thread{
     ...
     public enum State {
         NEW,
         RUNNABLE,
         BLOCKED,
         WAITING,
         TIMED_WAITING,
         TERMINATED;
     }
     ...
}

线程的6种状态总结

NEW: 新建状态,线程还没有启动
RUNNABLE: 可以运行状态,线程调用了start()方法后处于这个状态
BLOCKED: 锁阻塞状态,没有获取到锁处于这个状态
WAITING: 无限等待状态,线程执行时被调用了wait方法处于这个状态
TIMED_WAITING: 计时等待状态,线程执行时被调用了sleep(毫秒)或者wait(毫秒)方法处于这个状态
TERMINATED: 终止状态, 线程执行完毕或者遇到异常时,处于这个状态。

几种状态之间切换关系图
在这里插入图片描述

  • 17
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值