Java的线程状态
从操作系统的角度看,线程有5种状态:创建, 就绪, 运行, 阻塞, 终止(结束)。如下图所示
而Java定义的线程状态有: 创建(New), 可运行(Runnable), 阻塞(Blocked), 等待(Waiting), 计时等待(Time waiting) 被终止(Terminated)。
那么相比起操作系统的线程状态, Java定义的线程状态该如何解读呢? 如下:
1. Java的阻塞、 等待、 计时等待都属于操作系统中定义的阻塞状态,不过做了进一步的划分,阻塞(Blocked)是试图获得对象锁(不是java.util.concurrent库中的锁),而对象锁暂时被其他线程持有导致的;等待(Waiting)则是调用Object.wait,Thread.join或Lock.lock等方法导致的;计时等待(Time waiting)则是在等待的方法中引入了时间参数进入的状态,例如sleep(s)
2. Java的Runnable状态实际上包含了操作系统的就绪和运行这两种状态, 但并没有专门的标识进行区分,而是统一标识为Runnable
获取当前线程的状态和名称
currentThread()是Thread类的一个静态方法,它返回的是当前线程对象
对某个线程对象有以下方法:
- getState方法:返回该线程的状态,可能是NEW, RUNNABLE, BLOCKED, WAITING, TIME_WAITING, TEMINATED之一
- getName: 返回线程名称
- getPriority: 返回线程优先级
下面的例子中,我们通过Thread.currentThread()获取到当前线程对象, 并打印它的状态,名称和优先级:
public class MyThread extends Thread{
@Override
public void run() {
System.out.println("线程状态:" + Thread.currentThread().getState());
System.out.println("线程名称:" + Thread.currentThread().getName());
System.out.println("线程优先级:" + Thread.currentThread().getPriority());
}
public static void main (String args []) {
MyThread t = new MyThread();
t.start();
}
}
输出:
线程状态:RUNNABLE 线程名称:Thread-0 线程优先级:5
Java多线程之线程协作
常见的线程协作方式是:生产者/消费者。
一个线程作为生产者,生产要处理数据,比如拿一个线程来生产Order,用户每下一单,此线程就生产一个Order对象。设置一个仓库,来存放生产出来的Order对象。
一个线程作为消费者,消费|处理仓库中的Order对象(打印订单、拣货、发货)。
demo 订单处理流程
1、用一个类来封装要处理的数据
public class Order {
private int id;
//...
public Order(int id) {
this.id = id;
}
public int getId() {
return id;
}
//......
}
2、仓库
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class OrderStorage {
//使用阻塞队列作为仓库
private BlockingQueue<Order> queues;
//默认仓库容量为50
public OrderStorage() {
this.queues = new LinkedBlockingQueue<>(50);
}
//初始化仓库容量
public OrderStorage(int capacity) {
this.queues = new LinkedBlockingQueue<>(capacity);
}
//入库
public void push(Order order) throws InterruptedException {
queues.put(order);
}
//出库
public Order pop() throws InterruptedException {
return queues.take(); //仓库中没元素时,会阻塞当前线程,直到有元素可弹出。要不怎么叫BlockingQueue
}
}
java.util.concurrent包下有很多并发要使用的类,我们使用的阻塞队列就是这个包下的。concurrent 并发队列是先进先出的,先生产的先放到仓库中,先处理。
3、生产者
public class ProducerThread extends Thread{
public static OrderStorage orderStorage = new OrderStorage(100); //仓库容量为100
@Override
public void run() {
int i=1;
while (true){
Order order = new Order(i);
try {
orderStorage.push(order); //放到仓库中
System.out.println("已生产编号为"+i+"的订单");
Thread.sleep(1000); //降低速度,方便看效果
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
}
}
容量为100,不是说只能生成50个Order,生产的同时消费者也在消费仓库中的Order。
4、消费者
public class ConsumerThread extends Thread {
private OrderStorage orderStorage=ProducerThread.orderStorage; //消费者和生产者使用的要是同一个仓库
@Override
public void run() {
while (true){
try {
Order order = orderStorage.pop(); //如果仓库中没有Order,会阻塞当前线程,直到有Order可以弹出
System.out.println("已消费|处理编号为"+order.getId()+"的订单");
Thread.sleep(1000); //降低速度,方便看效果
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
5、测试类
public class Test {
public static void main(String[] args) {
ProducerThread producerThread = new ProducerThread();
producerThread.start();
ConsumerThread consumerThread = new ConsumerThread();
consumerThread.start();
}
}
6、效果
有时候一个订单,会先打印“消费”,再打印“生产”。
一个Order处理顺序一定是:生产 、入库 -> 出库、消费,要先放到仓库中才能从仓库中获取到。
只是说生产者、消费者2个线程执行sout时获取到时间片的先后不同。
接着咱们再来介绍多线程之线程池
//创建并返回一个线程池
ExecutorService es = Executors.newSingleThreadExecutor(); //此线程池只能容纳一个线程。放入的线程会依次执行,上一个执行完毕,才会执行下一个。
// ExecutorService es = Executors.newFixedThreadPool(10); //指定能容纳的最大线程数,达到最大数后无法扩容。这个用得最多
// ExecutorService es = Executors.newScheduledThreadPool(10); //指定线程池的初始大小,达到初始值后可以继续放入线程,会自动扩容,最大线程数受限于JVM的资源
// ExecutorService es = Executors.newCachedThreadPool(); //最大线程数受限于jvm的资源,如果一个线程空闲多少秒(默认60s),会自动回收该线程
//线程要自己放进去
es.execute(new Thread1()); //往线程池中放入一条线程,并开始执行此线程
es.execute(new Thread2()); //可以是extends Thread的类,也可以是实现Runnable接口的类
//es.submit(new Thread2()); //submit()和execute()作用差不多,区别是execute没有返回值(void),submit有返回值
//es.shutdown(); //停用线程池。如果不停用,即使线程池中的线程都执行完毕,线程池还在
new一个线程,只是创建一个对象,并没有分配线程所需的资源。
调用start()才开始分配线程所需的资源,并启动线程。
线程池预先给线程分配好一个线程所需的资源,把一个线程放进去,会自动把分配好的线程资源给它,然后启动该线程,
线程执行完毕时,会回收这条线程的线程资源,分配给新放入的线程。
线程池减少了创建、销毁线程的时间开销。
线程池的容量为n,并不是只能放n个线程,放不下的线程会自动放到队列中,线程池中的某个线程执行完毕,会自动把对列中的第一个线程推到线程池中(开始执行)。
如果队列也放不下,那就真放下了。
线程池中的线程都是并发执行的。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
导入的类是java.util.concurrent包下的,concurrent 并发,java.util.concurrent这个包中包含了很多并发要使用的类。
喜欢请多多点赞评论转发,你们的支持就是小编最大的动力!!!