Java多线程篇- 202-05-12 21:20
categories:
- java
- 多线程
一、实现线程的4种方式
1.继承Thread类
2.实现Runnable接口
优雅实现Runnable类的两种方式
1)使用内部匿名类实现,demo:com.learn.test.demo.myThread.runnable.AnonymousCreateDemo
2)使用Lambda表达式实现,demo:
com.learn.test.demo.myThread.runnable.LambdaCreateThreadDemo
注:使用内部匿名类和使用lambda表达式实现的区别并不大
实现Runnable类创建线程的优缺点
缺点:
1.无法直接访问Thread类相关属性和方法,也无法直接控制线程,需要借助Thread类的静态方法currentThread获取当前线程来实现相关操作
2.实现Runnable的类并不是线程类,而是线程类的target执行目标类,使用时需要将实现类的实例作为入参传入Thread类的构造,实现类的run方法才可以被调用 优点: 1.解耦,可扩展性好
2.适用于多个线程需要共享资源的场景,例如库存的增减 demo:com.learn.test.demo.myThread.ThreadDemo.goodsStockTest
3.使用Callable和FutureTask类创建线程池
线程池创建线程的步骤
1.实现Callable接口
2.将Callable接口的实现类作为FutureTask构造的入参传入
3.将FutureTask实例作为target属性传入Thread类的构造
4.调用Thread类的start方法,启动线程 demo:com.learn.test.demo.myThread.ThreadDemo.callable
实现调用链路
Thread.start()->Thread.run()->Callable实现类.call()->将call接口执行结果赋值给FutureTask的属性outcome FutureTask.get()->outcome属性
实现逻辑(详情可参见 尼恩 《Java并发核心编程(卷2)》多线程篇):
FutureTask是RunnableFuture的默认实现类,RunnableFuture是继承了Runnable和Future,所以FutureTask同时具有Runnable的run方法和Future的get方法,
具有异步执行和获取异步返回值的能力。在调用FutureTask的run方法时,run方法内部会回调Future的call方法。
4.使用线程池
使用线程池创建线程(实际生产中禁止使用Executors创建线程)
1.execute和submit方法的区别:
(入参)execute的入参是Runnable的实例,submit的入参可以是带FutureTask的实例也可以是Runnable的实例还可以是Thread的实例。
(返回值)execute无返回值,submit有返回值。
execute是Executors的方法,submit是子类ExecutorService的方法。
继承Thread类和实现Runnable类的区别
1.继承Thread类可以更好的实现多线程的并发,使每个线程更专注的完成各自的任务
2.实现Runnable类可以更好的实现多个线程并发的完成同一个任务,访问共享资源
3.当实现Runnable类并且多个线程共享资源时,需要使用原子类数据类型或是限制同步操作
继承Thread类和实现Runnable类的缺点
两者都没有返回异步执行结果,在某些需要关心返回值的场景下,这两种创建方式就不适用了
二、线程优先级
线程优先级的产生其实是跟CPU调度方式有关,目前CPU主流的调度方法是:基于CPU时间片进行线程调度。线程的调度一般分为两种:分时调度模型和抢占式调度模型。JAVA使用抢占式调度方式,也就是根据线程优先级高低来决定优先级被调用的级别。
JAVA线程优先级范围是从1到10,默认线程优先级是5,最大10,最小1,优先级越高越会被优先执行。Thread类中定义了3个常量值,表示线程的最高,最低和默认值。新建的线程优先级默认是5。
/**
* The minimum priority that a thread can have.
*/
public final static int MIN_PRIORITY = 1;
/**
* The default priority that is assigned to a thread.
*/
public final static int NORM_PRIORITY = 5;
/**
* The maximum priority that a thread can have.
*/
public final static int MAX_PRIORITY = 10;
三、线程状态
1、JAVA的6种线程状态
Thread类中定义了State枚举类,State枚举里定义了JAVA的6种线程状态:NEW(新建状态)、RUNNABLE(运行状态)、BLOCKED(阻塞状态)、WAITING(等待状态)、TIMED_WAITING(超时等待状态)、TERMINATED(终止状态)。
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
NEW(新建状态)
线程被new出来后,还未调用start方法时,线程处于新建状态。
RUNNABLE(运行状态)
当线程调用start方法后,java线程状态就会从NEW变成RUNNABLE状态,RUNNABLE其实对应内核线程的两种状态:就绪和运行状态,而JAVA中是没有就绪这个状态的。
当JAVA线程的Thread实例调用start方法后,JAVA线程会进入RUNNABLE状态,而JAVA线程对应的内核线程会进入就绪状态,等待CPU分配时间片,等待CPU调用执行,线程会一直在就绪和运行状态来回切换,直到线程执行完成或是因为异常而终止。
BLOCKED(阻塞状态)
WAITING(等待状态)
TIMED_WAITING(限时等待状态)
使线程进入TIMED_WAITING状态的几种操作
- Thread.sleep(int n):使得当前线程进入限时等待状态,等待时间为n毫秒。
- Object.wait():带时限的抢占对象的monitor锁。
- Thread.join():带时限的线程合并。
- LockSupport.parkNanos():让线程等待,时间以纳秒为单位。
- LockSupport.parkUntil():让线程等待,时间可以灵活设置。
TERMINATED(终止状态)
当执行完线程实例run()方法后,线程就会进入TERMINATED状态。如果线程在执行过程中出现异常并没有别捕获到异常,也会使线程直接因为异常导致终止进入TERMINATED状态。2、使用Jstack工具查看线程状态
命令
Jstack <pid> //pid java进程ID,可用jps命令查看
四、线程核心方法
1.合并线程:join()
什么是合并线程?
简单来说有两个线程 线程A依赖线程B执行结果,线程A在执行过程中去调用线程B的join()方法,然后线程A让出CPU等待线程B执行完毕,线程A再继续执行。线程A就是合并线程,线程B就是被合并线程。怎么使用合并线程?
join()方法有3种重载方式:join方法没有返回值,无法直观的知道被合并线程的执行结果 ```java /**
- 无入参:合并线程等待被合并线程执行完毕 */ public final void join() throws InterruptedException;
/**
- 入参:millis 合并线程等待时间
- 合并线程在等待指定millis后,不论被合并线程是否执行完毕,都不再等待 / public final synchronized void join(long millis) throws InterruptedException; /*
- 入参:millis 合并线程等待时间
- 入参:nanos 额外的纳秒等待
- 合并线程在等待指定millis+nanos后,不论被合并线程是否执行完毕,都不再等待 */ public final synchronized void join(long millis, int nanos)throws InterruptedException;
<a name="wZ3A9"></a>
#### 无返回结果Demo
```java
/**
* join方法:合并线程
* 在线程A中去调用线程B的join方法,那么线程A就是主动合并线程,线程B就是被动合并线程。
* join有3种重载方式
*
* @author Bai
* @date 2022/5/23 22:38
*/
public class JoinDemo {
static class SleepThread extends Thread {
public SleepThread (String name) {
super(name);
}
@Override
public void run () {
PrintUtils.print(getName() + "开始执行了");
SleepUtils.sleep(2000L);
PrintUtils.print(getName() + "执行结束了");
}
}
public static void main (String[] args) {
PrintUtils.print("main-执行了");
SleepThread sleepThread1 = new SleepThread("sleepThread1");
try {
sleepThread1.start();
sleepThread1.join();
} catch (InterruptedException e) {
}
SleepThread sleepThread2 = new SleepThread("sleepThread2");
try {
sleepThread2.start();
sleepThread2.join(1000); //等待执行1000ms后继续执行main线程
} catch (InterruptedException e) {
}
SleepThread sleepThread3 = new SleepThread("sleepThread3");
try {
sleepThread3.start();
sleepThread3.join(1000, 1000);//等待执行1000ms+1000nanos后继续执行main线程
} catch (InterruptedException e) {
}
PrintUtils.print("main-执行结束了");
}
}
main-执行了
sleepThread1开始执行了
sleepThread1执行结束了
sleepThread2开始执行了
sleepThread3开始执行了
main-执行结束了
sleepThread2执行结束了
sleepThread3执行结束了