java多线程使用详解与案例,超详细
一、创建线程的方式
1、继承Thread类
- 让子类继承Thread线程类
- 子类必须重写Thread类的run方法
- 创建一个自己定义的线程对象
- 调用start()方法启动线程
//测试类
/**
* 1、让子类继承Thread线程类
*/
public class ThreadTest1 extends Thread {
//2、子类必须重写Thread类的run方法
@Override
public void run() {
for (int i = 1;i <=5;i++){
System.out.println("myThread线程输出" + i);
}
}
}
//主线程main函数
public class Main {
//main方法是由一条默认的主线程负责执行的
public static void main(String[] args) {
//3、创建一个自己定义的线程对象
Thread t = new ThreadTest1();
//4、启动线程
//注意是调用start方法而不是run方法,调用start方法是告诉系统要把t对象单独开一条线程
//如果调用run方法则是调用一个普通对象的一个方法,不会另开一条线程
t.start();
for (int i = 1;i <=5;i++){
System.out.println("主线程main输出" + i);
}
}
}
主线程main输出1
myThread线程输出1
主线程main输出2
myThread线程输出2
主线程main输出3
myThread线程输出3
主线程main输出4
myThread线程输出4
主线程main输出5
myThread线程输出5
进程已结束,退出代码0
结果:此时程序有两条线程main和t,这两条线程交替执行。
优点:编码简单。
缺点:由于该方式要继承Thread类,所以就不能继承其他类,不利于功能扩展,不灵活。
2、实现Runnable接口
- 创建一个任务类实现Runnable方法
- 实现Runnable接口的run方法
- 创建一个任务类对象
- 把任务对象交给Thread线程类处理
- 调用线程对象start方法启动线程
//任务类
/**
* 1、定义一个任务类,实现Runnable接口
*/
public class MyRunnable implements Runnable{
//2、重写Runnable的run方法
@Override
public void run() {
for (int i = 1;i <=5;i++){
System.out.println("MyRunnable==》" + i);
}
}
}
@Test
public void RunnableTest(){
//3、创建任务对象
//注意这个不是一个线程对象,而是任务对象
Runnable runnable = new MyRunnable();
//4、把任务对象交给一个线程对象处理
//thread有一个Runnable类型的有参构造:public Thread(Runnable target)
//thread接受到参数后会自动创建一个匿名线程
new Thread(runnable).start();
for (int i = 1;i <=5;i++){
System.out.println("主线程main==》" + i);
}
}
MyRunnable==》1
MyRunnable==》2
主线程main==》1
MyRunnable==》3
主线程main==》2
MyRunnable==》4
主线程main==》3
MyRunnable==》5
主线程main==》4
主线程main==》5
进程已结束,退出代码0
优点:任务类只是实现接口,还可以继续继承其他类,实现其他接口,扩展性强。
缺点:需要多个Runnable对象。
扩展
由于每次创建线程都要创建一个实现接口的类,非常麻烦,可以用匿名内部类或Lambda表达式代替
@Test
public void RunnableTest2(){
//匿名内部类写法1
Runnable runnable = new Runnable() {
@Override
public void run() {
for (int i = 1;i <=5;i++){
System.out.println("MyRunnable==》" + i);
}
}
};
new Thread(runnable).start();
for (int i = 1;i <=5;i++){
System.out.println("主线程main==》" + i);
}
}
@Test
public void RunnableTest2(){
//匿名内部类写法2
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1;i <=5;i++){
System.out.println("MyRunnable==》" + i);
}
}
}).start();
for (int i = 1;i <=5;i++){
System.out.println("主线程main==》" + i);
}
}
@Test
public void RunnableTest2(){
//Lambda 表达式的写法
new Thread(()->{
for (int i = 1;i <=5;i++){
System.out.println("子线程==》" + i);
}
}).start();
for (int i = 1;i <=5;i++){
System.out.println("主线程main==》" + i);
}
}
3、实现Callable
前面几种方法创建的线程均没有返回值,假如线程执行完毕后需要返回,可以用这种方式。
- 定义一个实现Callable接口的类,指定返回数据的类型
- 重写call方法,指定返回类型
- 创建一个Callable对象,注意该对象不是还不是任务对象
- 把Callable类型的对象封装成FutureTask类型的对象
- 把任务对象交给Thread对象
- 获取线程执行完毕后返回的结果
/**
* 1、定义一个实现Callable接口的类,指定返回数据的类型
*/
public class MyCallable implements Callable<String> {
int n;
public MyCallable(int n){
this.n = n;
}
// 2、重写call方法,指定返回类型
@Override
public String call() throws Exception {
int sum = 0;
for(int i = 1; i < this.n; i++){
sum += i;
}
return "1 - " + n + "的和为:" + sum;
}
}
@Test
public void CallableTest() throws ExecutionException, InterruptedException {
// 3、创建一个Callable对象,注意该对象不是还不是任务对象
Callable<String> callable= new MyCallable(10);
// 4、把Callable类型的对象封装成FutureTask类型的对象
// futureTask才是线程任务对象
FutureTask<String> futureTask = new FutureTask<>(callable);
// 5、把任务对象交给Thread对象
new Thread(futureTask).start();
// 6、获取线程执行完毕后返回的结果
// 注意:如果上面线程还没执行完毕就执行到该条语句,则主线程将会进入阻塞状态,
// 等待该子线程执行完毕才开始运行
String s1 = futureTask.get();
System.out.println(s1);
}
1 - 10的和为:45
进程已结束,退出代码0
优点:线程任务类只是实现接口,可以继续继承类和实现接口,可扩展性强,
可以在线程执行完毕就去获取线程执行的结果。
缺点:代码复杂一点。
二、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对象为线程对象,并指定线程名称 |
三、线程安全
什么是线程安全问题?
答:多线程同时操作同一临界资源,导致结果存在的不准确性。
例题:当两个人同时对同一账号取钱,其其结果导致第一个人把前全部取走,二第二个人还可以继续取钱。
/**
* 共享资源:账户余额
*/
public class Accoud {
private double accound;
public Accoud(double accound) {
this.accound = accound;
}
//取钱操作
public void drowMoney(double i) {
// 获取当前取钱人的名字
String name = Thread.currentThread().getName();
if(accound < i){
System.out.println(name + "来取钱,余额不足");
}
else{
System.out.println(name + "取出额度为:" + i);
accound -= i;
System.out.println(name + "取钱后剩余余额为:" + accound);
}
}
}
public class ThreadTest2 extends Thread{
Accoud accoud;
public ThreadTest2(Accoud accoud,String name) {
super(name);
this.accoud = accoud;
}
@Override
public void run() {
//取钱
accoud.drowMoney(1000);
}
}
public class Main {
public static void main(String[] args) {
//创建临界资源
Accoud accoud = new Accoud(1000);
//创建两个线程,同时访问accoud资源
new ThreadTest2(accoud,"小红").start();
new ThreadTest2(accoud,"小蓝").start();
}
}
小红取出额度为:1000.0
小蓝取出额度为:1000.0
小红取钱后剩余余额为:0.0
小蓝取钱后剩余余额为:-1000.0
进程已结束,退出代码0
四、线程同步
什么是线程同步?
答:线程同步是指一个或多个线程必须等待某个或多个线程执行完某些操作后才能继续往下执行,常用于多线程同时操作同一临界资源问题,但某线程在操作某一临界资源时,其他线程必须等待。
实现线程同步有三种方法:同步代码块、同步方法、lock锁
1、同步代码块
作用:把访问共享资源的核心代码给上锁,但此线程执行完后会自动解锁,以此保证线程安全。
synchronized(同步锁){
访问共享资源的核心代码
}
对之前取钱操作加同步锁
public void drowMoney(double i) {
// 获取当前取钱人的名字
String name = Thread.currentThread().getName();
synchronized ("这是同步锁") {
if(accound < i){
System.out.println(name + "来取钱,余额不足");
}
else{
System.out.println(name + "取出额度为:" + i);
accound -= i;
System.out.println(name + "取钱后剩余余额为:" + accound);
}
}
}
小红取出额度为:1000.0
小红取钱后剩余余额为:0.0
小蓝来取钱,余额不足
注意:这个锁其实是一个对象,这个对象被引用的时候会被标记,解锁后才会消除标记
所以就有一个问题,如果有小红、小蓝、小黑、小白,四个线程,而业务只需要小红小蓝同步,小黑小白同步,如果锁对象是唯一的,则所有的线程都用同一把锁,则红蓝黑白都会被同步,明明两组不相干的操作却要一起同步等待,就会影响效率,所以要想把两组同步分开,就要加不同的锁,用 this
public class Main {
public static void main(String[] args) {
Accoud accoud1 = new Accoud(1000);
new ThreadTest2(accoud1,"小红").start();
new ThreadTest2(accoud1,"小蓝").start();
Accoud accoud2 = new Accoud(1000);
new ThreadTest2(accoud2,"小黑").start();
new ThreadTest2(accoud2,"小白").start();
}
}
public void drowMoney(double i) {
// 获取当前取钱人的名字
String name = Thread.currentThread().getName(
//如果小红小蓝线程调用这个方法,则这里的this是指accoud1对象
//如果小黑小白线程调用这个方法,则这里的this是指accoud2对象
synchronized (this) {
if(accound < i){
System.out.println(name + "来取钱,余额不足");
}
else{
System.out.println(name + "取出额度为:" + i);
accound -= i;
System.out.println(name + "取钱后剩余余额为:" + accound);
}
}
}
小白取出额度为:1000.0
小蓝取出额度为:1000.0
小白取钱后剩余余额为:0.0
小蓝取钱后剩余余额为:0.0
小黑来取钱,余额不足
小红来取钱,余额不足
用this可以识别调用不同对象的线程,但如果要在静态方法里上锁,因为静态方法是直接用类名调用的,不需要创建对象,这时可以用这个类的唯一标识当锁,而这个类的唯一标识是字节码(类名.class)
public static void drowMoney(double i) {
// 获取当前取钱人的名字
String name = Thread.currentThread().getName();
//静态方法用类的字节码标识
synchronized (Accoud.class) {
if(accound < i){
System.out.println(name + "来取钱,余额不足");
}
else{
System.out.println(name + "取出额度为:" + i);
accound -= i;
System.out.println(name + "取钱后剩余余额为:" + accound);
}
}
}
锁对象的使用规范
- 建议使用共享资源作为锁对象,对于实例方法建议使用this作为锁对象。
- 对于静态方法建议使用字节码(类名.class)对象作为锁对象。
2、同步方法
作用:把访问共享资源的核心方法给上锁,以此保证线程安全。
修饰符 synchronized 返回值类型 方法名(形参列表){
操作共享资源的代码
}
同步方法底层原理
- 同步方法其实底层也是隐式锁对象的,只是锁的范围是整个方法代码。
- 如果方法是实例方法:同步方法默认用this作为的锁对象。
- 如果方法是静态方法:同步方法默认用类名.class作为锁对象。
public synchronized void drowMoney(double i) {
// 获取当前取钱人的名字
String name = Thread.currentThread().getName();
if(accound < i){
System.out.println(name + "来取钱,余额不足");
}
else{
System.out.println(name + "取出额度为:" + i);
accound -= i;
System.out.println(name + "取钱后剩余余额为:" + accound);
}
}
3、Lock锁
Lock锁是JDK5开始提供的一个新的锁定操作,通过它可以创建出锁对象进行加锁和解锁,更灵活、更方便。
Lock是接口,不能直接实例化,可以采用它的实现类ReentrantLock来构建Lock锁对象。
Lock常用方法
变量类型 方法名称 说明 void lock() 获得锁。 void lockInterruptibly () 除非当前线程是interrupted,否则获取锁定。 Condition newCondition() 返回一个新Condition绑定到该实例Lock实例。 boolean tryLock () 只有在调用时他是空闲的才能获取锁。 boolean tryLock (long time,TimeUnit unit) 如果锁在给定的等待时间内是空闲的并且当前线程不是interrupted,则获取锁。 void unlock () 释放锁定。
五、线程池
什么是线程池?
答:线程池就是一个可以复用线程的技术。
不使用线程池的问题
用户每发起一个请求,后台就需要创建一个新线程来处理,下次新任务来了肯定又要创建新线程处理的,而创建新线程的开销是很大的,并且请求过多时,肯定会产生大量的线程出来,这样会严重影响系统的性能。
谁代表线程池?
答:JDK5.0起提供了代表线程池的接口:ExecutorService。
如何得到线程池对象?
- 方式一:使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象,
- 方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象。
1、ThreadPoolExecutor构造器
public ThreadPoolExecutor(int corePoolsize, //指定线程池的核心线程的数量
int maximumPoolsize,//指定线程池的最大线程数量
long keepAliveTime,//指定临时线程的存活时间。
TimeUnit unit, //指定临时线程存活的时间单位(秒、分、时、天)
BlockingQueue<Runnable>workQueue,//指定线程池的任务队列
ThreadFactory threadFactory,//指定线程池的线程工厂
RejectedExecutionHandler handler)//指定线程池的任务拒绝策略
//(线程都在忙,任务队列也满了的时 //候,新任务来了该怎么处理
关于核心线程池的数量指定多少合适?
- 对于计算机密集型的任务(使用CPU频繁),主机逻辑处理器核数 + 1
- 对于IO密集型的任务,主机逻辑处理器核数 * 2
public class Main {
public static void main(String[] args) {
//1、通过TreadPoolExecutor创建一个线程池对象,
ExecutorService pool = new ThreadPoolExecutor(3, 5, 8,
TimeUnit.SECONDS, new ArrayBlockingQueue<>(4),
Executors.defaultThreadFactory(),//使用默认的线程池创建方法
new ThreadPoolExecutor.AbortPolicy());
}
}
线程池的注意事项
1、临时线程什么时候创建?
答:新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。
2、什么时候会开始拒绝新任务?
答:核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始拒绝任务。
2、ExecutorService的常用方法
方法名称 | 说明 |
---|---|
void execute(Runnable comand) | 执行 Runnable 任务 |
Future submit(Callable task) | 执行 Callable 任务,返回未来任务对象,用于获取线程返回的结果 |
void shutdown() | 等全部任务执行完毕后,再关闭线程池 |
List shutdownNow | 立即关闭线程池,停止正在执行的任务,并返回队列中未执行任务 |
public class MyRunable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "正在工作");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3、线程池处理 Runnable任务
public class Main {
public static void main(String[] args) {
//1、通过TreadPoolExecutor创建一个线程池对象,
ExecutorService pool = new ThreadPoolExecutor(3, 5, 8,
TimeUnit.SECONDS, new ArrayBlockingQueue<>(4),
Executors.defaultThreadFactory(),//使用默认的线程池创建方法
new ThreadPoolExecutor.AbortPolicy());
Runnable target = new MyRunable();
// 线程池最开始初始化时是没有线程的
// 当某线程任务结束后线程池不会自动销毁线程
// 当线程池接受到新任务后如果没有空闲线程会自动创建新线程处理这个任务
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
}
}
pool-1-thread-3正在工作
pool-1-thread-2正在工作
pool-1-thread-1正在工作
pool-1-thread-3正在工作
pool-1-thread-2正在工作
//线程一旦创建线程池就不会自动关闭,所以整个程序会一直处于运行状态不会停
添加关闭线程的方法
public class Main {
public static void main(String[] args) {
//1、通过TreadPoolExecutor创建一个线程池对象,
ExecutorService pool = new ThreadPoolExecutor(3, 5, 8,
TimeUnit.SECONDS, new ArrayBlockingQueue<>(4),
Executors.defaultThreadFactory(),//使用默认的线程池创建方法
new ThreadPoolExecutor.AbortPolicy());
Runnable target = new MyRunable();
// 线程池最开始初始化时是没有线程的
// 当某线程任务结束后线程池不会自动销毁线程
// 当线程池接受到新任务后如果没有空闲线程会自动创建新线程处理这个任务
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.shutdown();//等全部任务执行完毕后,再关闭线程池
// pool.shutdownNow();//立即关闭线程池,停止正在执行的任务,并返回队列中未执行任务
}
}
pool-1-thread-2正在工作
pool-1-thread-1正在工作
pool-1-thread-3正在工作
pool-1-thread-2正在工作
pool-1-thread-1正在工作
进程已结束,退出代码0
临时线程的创建时机
- 当任务队列未满时不创建临时线程
public class Main {
public static void main(String[] args) {
ExecutorService pool = new ThreadPoolExecutor(3, 5, 8,
TimeUnit.SECONDS, new ArrayBlockingQueue<>(4),
Executors.defaultThreadFactory(),//使用默认的线程池创建方法
new ThreadPoolExecutor.AbortPolicy());
Runnable target = new MyRunable();
//模拟临时线程创建时机
//3个核心线程在忙
pool.execute(target);
pool.execute(target);
pool.execute(target);
// 4个任务队列满了
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
//如果还有任务请求,则开始创建临时线程
// pool.execute(target);
pool.shutdown();//等全部任务执行完毕后,再关闭线程池
// pool.shutdownNow();//立即关闭线程池,停止正在执行的任务,并返回队列中未执行任务
}
}
pool-1-thread-3正在工作
pool-1-thread-1正在工作
pool-1-thread-2正在工作
pool-1-thread-1正在工作
pool-1-thread-2正在工作
pool-1-thread-3正在工作
pool-1-thread-1正在工作
进程已结束,退出代码0
- 当核心线程在忙且任务队列已满时创建创建临时线程
package org.example;
import org.example.ExecutorService.MyRunable;
import java.util.concurrent.*;
public class Main {
public static void main(String[] args) {
//1、通过TreadPoolExecutor创建一个线程池对象,
ExecutorService pool = new ThreadPoolExecutor(3, 5, 8,
TimeUnit.SECONDS, new ArrayBlockingQueue<>(4),
Executors.defaultThreadFactory(),//使用默认的线程池创建方法
new ThreadPoolExecutor.AbortPolicy());
Runnable target = new MyRunable();
//模拟临时线程创建时机
//3个核心线程在忙
pool.execute(target);
pool.execute(target);
pool.execute(target);
// 4个任务队列满了
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
//如果还有任务请求,则开始创建临时线程
pool.execute(target);
pool.execute(target);
pool.shutdown();//等全部任务执行完毕后,再关闭线程池
// pool.shutdownNow();//立即关闭线程池,停止正在执行的任务,并返回队列中未执行任务
}
}
pool-1-thread-1正在工作
pool-1-thread-2正在工作
pool-1-thread-3正在工作
pool-1-thread-4正在工作//创建了4、5的临时线程
pool-1-thread-5正在工作
pool-1-thread-1正在工作
pool-1-thread-3正在工作
pool-1-thread-4正在工作
pool-1-thread-2正在工作
进程已结束,退出代码0
新任务拒绝策略
- 当核心线程和临时线程在满,且任务列表满时,再有任务请求则调用拒绝策略
- 常用策略
策略 | 详解 |
---|---|
ThreadPoolExecutor.AbortPolicy | 丢弃任务并抛出RejectedExecutionException.异常。是默认的策略 |
ThreadPoolExecutor.DiscardPolicy: | 丢弃任务,但是不抛出异常这是不推荐的做法 |
ThreadPoolExecutor.DiscardoldestPolicy | 抛弃队列中等待最久的任务然后把当前任务加入队列中 |
ThreadPoolExecutor.CallerRunsPolicy | 由主线程负责调用任务的ru0方法从而绕过线程池直接执行 |
public class Main {
public static void main(String[] args) {
//1、通过TreadPoolExecutor创建一个线程池对象,
ExecutorService pool = new ThreadPoolExecutor(3, 5, 8,
TimeUnit.SECONDS, new ArrayBlockingQueue<>(4),
Executors.defaultThreadFactory(),//使用默认的线程池创建方法
new ThreadPoolExecutor.CallerRunsPolicy());//拒绝策略
Runnable target = new MyRunable();
//模拟临时线程创建时机
//3个核心线程在忙
pool.execute(target);
pool.execute(target);
pool.execute(target);
// 4个任务队列满了
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
//如果还有任务请求,则开始创建临时线程
pool.execute(target);
pool.execute(target);
//还有任务请求会自动调用指定的拒绝策略
pool.execute(target);
pool.shutdown();//等全部任务执行完毕后,再关闭线程池
// pool.shutdownNow();//立即关闭线程池,停止正在执行的任务,并返回队列中未执行任务
}
}
//调用CallerRunsPolicy拒绝策略
pool-1-thread-2正在工作
main正在工作
pool-1-thread-1正在工作
pool-1-thread-5正在工作
pool-1-thread-3正在工作
pool-1-thread-4正在工作
pool-1-thread-1正在工作
pool-1-thread-2正在工作
pool-1-thread-4正在工作
pool-1-thread-3正在工作
进程已结束,退出代码0
4、线程池处理 Callable 任务
public class MyCallable implements Callable<String> {
int n;
public MyCallable(int n){
this.n = n;
}
// 2、重写call方法,指定返回类型
@Override
public String call() throws Exception {
int sum = 0;
for(int i = 1; i <= this.n; i++){
sum += i;
}
return Thread.currentThread().getName() + "求出》1-" + n + "的和为:" + sum;
}
}
public class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService pool = new ThreadPoolExecutor(3, 5, 8,
TimeUnit.SECONDS, new ArrayBlockingQueue<>(4),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy());
//使用线程处理Callable任务
Future<String> f1 = pool.submit(new MyCallable(100));
Future<String> f2 = pool.submit(new MyCallable(200));
Future<String> f3 = pool.submit(new MyCallable(300));
Future<String> f4 = pool.submit(new MyCallable(400));
System.out.println(f1.get());
System.out.println(f2.get());
System.out.println(f3.get());
System.out.println(f4.get());
pool.shutdown();//等全部任务执行完毕后,再关闭线程池
}
}
pool-1-thread-1求出》1-100的和为:5050
pool-1-thread-2求出》1-200的和为:20100
pool-1-thread-3求出》1-300的和为:45150
pool-1-thread-2求出》1-400的和为:80200
进程已结束,退出代码0
5、Executors 工具类实现线程池
- Executors 工具底层都是通过线程池的实现类ThreadPoolExecutor创建的线程池对象。
- 不建议使用这个Executors工具类实现线程池,因为在大型并发系统环境中使用Executors如果不注意可能会出现系统风险。用原始的创建方法可以让开发者更好的理解线程池执行流程,也可以控制线程池一些数据来规避风险。
- 常用方法
方法名称 | 说明 |
---|---|
public static ExecutorService newFixedThreadPool(int nThreads) | 创建固定线程数量的线程池,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程替代它。 |
public static ExecutorService newsingleThreadExecutor() | 创建只有一个线程的线程池对象,如果该线程出现异常而结束,那么线程池会补充一个新线程。 |
public static ExecutorService newCachedThreadPool() | 线程数量随着任务增加而增加,如果线程任务执行完毕且空闲了6s则会被回收掉。 |
public static ScheduledExecutorService newScheduledThreadPool(int corePoolsize) | 创建一个线程池,可以实现在给定的延迟后运行任务,或者定期执行任务。 |
六、线程的生命周期
Java总共定义了6种状态。
6种状态都定义在Thread类的内部枚举类中
public class Thread{
...
public enum State{
NEW, //新建状态
RUNNABLE, //运行状态
BLOCKED, //阻塞状态
WAITING, //计时等待状态
TIMED WAITING, //无限等待状态
TERMINATED; //终止状态
}
}
线程状态 | 说明 |
---|---|
NEW(新建) | 线程刚被创建,但是并未启动。 |
Runnable(可运行) | 线程已经调用了start(),等待CPU调度。 |
B1 ocked(锁阻塞) | 线程在执行的时候未竞争到锁对象,则该线程进入Blocked状态。 |
Waiting(无限等待) | 一个线程进入Waiting状态,另一个线程调用notify或者notifyAll方法才能够唤醒 |
Timed Waiting(计时等待) | 同waiting状态,有几个方法(sleep,wait)有超时参数,调用他们将进入Timed Waiting状态。 |
Teminated(被终止) | 因为ru方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。 |
注意得到锁的线程调用 sleep() 进入等待状态,期间不会释放锁,而 wait() 会释放锁。
java多线程使用详解与案例,超详细的更多相关文章
- Java多线程超级详解(只看这篇就够了)
多线程能够提升程序性能,也属于高薪必能核心技术栈,本篇会全面详解Java多线程.@mikechen 主要包含如下几点: 基本概念 很多人都对其中的一些概念不够明确,如同步.并发等等,让我们先建立一个数 ...
- Java多线程编程详解
转自:http://programming.iteye.com/blog/158568 线程的同步 由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题.Ja ...
- JAVA多线程synchronized详解
Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 当两个并发线程访问同一个对象object中的这个synchronized(this)同 ...
- Java多线程基础详解
基础概念进程进程是操作系统结构的基础:是一次程序的执行:是一个程序及其数据在处理机上顺序执行时所发生的活动.操作系统中,几乎所有运行中的任务对应一条进程(Process).一个程序进入内存运行,即变成 ...
- 【多线程】java多线程Completablefuture 详解【在spring cloud微服务之间调用,防止接口超时的应用】【未完成】
参考地址:https://www.jianshu.com/p/6f3ee90ab7d3 示例: public static void main(String[] args) throws Interr ...
- 大牛针对零基础入门c语言详解指针(超详细)
C语言指针说难不难但是说容易又是最容易出错的地方,因此不管是你要做什么只要用到C指针你就跳不过,今天咱们就以 十九个例子来给大家简单的分析一下指针的应用,最后会有C语言视频资料提供给大家更加深入的参考 ...
- Android开发之常用框架WebView详解代码。超详细,送给初学者,完全掌握此控件
这是我特意为新手小白写的一个代码,教大家完完全全掌握WebView, 我感觉,你看懂这个,基本上可以满足以后工作中的需要了,(只针对Webview的使用),但是其实它还有好多功能,比如真正的设计到和H ...
- Java多线程——多线程方法详解
本系列文章是Java多线程的详解介绍,对多线程还不熟悉的同学可以先去看一下我的这篇博客Java基础系列3:多线程超详细总结,这篇博客从宏观层面介绍了多线程的整体概况,接下来的几篇文章是对多线程的深入剖 ...
- Java :内部类基础详解
可以将一个类的定义放在另一个类的定义内部,这就是内部类. 第一次见面 内部类我们从外面看是非常容易理解的,无非就是在一个类的内部在定义一个类. public class OuterClass { pr ...
- Springboot 整合 Dubbo/ZooKeeper 详解 SOA 案例
摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢! “看看星空,会觉得自己很渺小,可能我们在宇宙中从来就是一个偶然.所以,无论什么事情,仔细想一 ...
随机推荐
- 2021-12-28:给定一个二维数组matrix,matrix[i][j] = k代表: 从(i,j)位置可以随意往右跳<=k步,或者从(i,j)位置可以随意往下跳<=k步, 如果matrix[i]
2021-12-28:给定一个二维数组matrix,matrix[i][j] = k代表: 从(i,j)位置可以随意往右跳<=k步,或者从(i,j)位置可以随意往下跳<=k步, 如果mat ...
- 去掉DosBox烦人的Status Windows
首先我们上成品动态图 很干净,很清爽有没有! 步骤 1.找到并选中DosBox快捷方式,鼠标右键点击选择属性 2.修改目标后面的参数,默认是 -userconf ,再添加一个 -noconsole 就 ...
- Ubuntu 18.04 (Bionic) 简单快速的安装mongodb
按步骤走,不带脑子式安装(注意4.0版本mongodb官方已经不再支持,以下代码中可以修改mongodb版本号安装,目前最新版为6.0,如果懒得改直接用也可以,文章后边第三章第一条代码会直接升级为最新 ...
- vue平铺日历组件之按住ctrl、shift键实现跨月、跨年多选日期的功能
已经好久没有更新过博客了,大概有两三年了吧,因为换了工作,工作也比较忙,所以也就没有时间来写技术博客,期间也一直想写,但自己又比较懒,就给耽误了.今天这篇先续上,下一篇什么时候写,我也不知道,随心所欲 ...
- linux nfs共享存储服务
目录 一.nfs服务 二.nfs优点 三.配置文件 四.共享文件配置过程 五.实验 1.创建共享文件(两台终端共享) 一.nfs服务 概念:网络上共享文件系统的协议,运行多个服务器之间通过网络共享文件 ...
- 计算机网络 ACL和ANT
目录 一.ACL概况 二.ACL工作过程 三.ACL实验 四.ANT概况 五.ANT工作过程 六.ANT实验 一.ACL概况 概念:主要是对报文进行区分,路由器会对报文进行检查,查看是否符合通过标准或 ...
- Android SmartTable表格框架1_如何使用
SmartTable github地址:https://github.com/huangyanbin/smartTable 参考:https://juejin.cn/post/684490354910 ...
- 魔力屏障 (magic) 题解
魔力屏障 (magic) [问题描述] 小 Z 生活在神奇的魔法大陆上.今天他的魔法老师给了它这样一个法阵作为它 的期末考试题目: 法阵由从左至右 n 道魔力屏障组成,每道屏障有一个临界值 a,如果它 ...
- 如何安装旧版本的 R 包
由于微信不允许外部链接,你需要点击文章尾部左下角的 "阅读原文",才能访问文中链接. 我们在安装 R 包的时候,经常会发现某个最新的包与当前 R 的版本不兼容. > inst ...
- 【理论积累】Python中的Pandas库【一】
Pandas库介绍 Pandas 是一个用于数据分析的 Python 第三方库,能够处理和分析不同格式的数据,例如:CSV.Excel.SQL 数据库等.Pandas 提供了两种数据结构,分别为 Se ...