一、创建线程的方式

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多线程使用详解与案例,超详细的更多相关文章

  1. Java多线程超级详解(只看这篇就够了)

    多线程能够提升程序性能,也属于高薪必能核心技术栈,本篇会全面详解Java多线程.@mikechen 主要包含如下几点: 基本概念 很多人都对其中的一些概念不够明确,如同步.并发等等,让我们先建立一个数 ...

  2. Java多线程编程详解

    转自:http://programming.iteye.com/blog/158568 线程的同步 由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题.Ja ...

  3. JAVA多线程synchronized详解

    Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 当两个并发线程访问同一个对象object中的这个synchronized(this)同 ...

  4. Java多线程基础详解

    基础概念进程进程是操作系统结构的基础:是一次程序的执行:是一个程序及其数据在处理机上顺序执行时所发生的活动.操作系统中,几乎所有运行中的任务对应一条进程(Process).一个程序进入内存运行,即变成 ...

  5. 【多线程】java多线程Completablefuture 详解【在spring cloud微服务之间调用,防止接口超时的应用】【未完成】

    参考地址:https://www.jianshu.com/p/6f3ee90ab7d3 示例: public static void main(String[] args) throws Interr ...

  6. 大牛针对零基础入门c语言详解指针(超详细)

    C语言指针说难不难但是说容易又是最容易出错的地方,因此不管是你要做什么只要用到C指针你就跳不过,今天咱们就以 十九个例子来给大家简单的分析一下指针的应用,最后会有C语言视频资料提供给大家更加深入的参考 ...

  7. Android开发之常用框架WebView详解代码。超详细,送给初学者,完全掌握此控件

    这是我特意为新手小白写的一个代码,教大家完完全全掌握WebView, 我感觉,你看懂这个,基本上可以满足以后工作中的需要了,(只针对Webview的使用),但是其实它还有好多功能,比如真正的设计到和H ...

  8. Java多线程——多线程方法详解

    本系列文章是Java多线程的详解介绍,对多线程还不熟悉的同学可以先去看一下我的这篇博客Java基础系列3:多线程超详细总结,这篇博客从宏观层面介绍了多线程的整体概况,接下来的几篇文章是对多线程的深入剖 ...

  9. Java :内部类基础详解

    可以将一个类的定义放在另一个类的定义内部,这就是内部类. 第一次见面 内部类我们从外面看是非常容易理解的,无非就是在一个类的内部在定义一个类. public class OuterClass { pr ...

  10. Springboot 整合 Dubbo/ZooKeeper 详解 SOA 案例

    摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢!   “看看星空,会觉得自己很渺小,可能我们在宇宙中从来就是一个偶然.所以,无论什么事情,仔细想一 ...

随机推荐

  1. 2021-10-16:单词拆分 II。给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,在字符串中增加空格来构建一个句子,使得句子中所有的单词都在词典中。返回所有这些可能的句子。

    2021-10-16:单词拆分 II.给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,在字符串中增加空格来构建一个句子,使得句子中所有的单词都在词典中.返回所有这些可能的句子. ...

  2. python 之路,Django rest framework 初探

    摘自 金角大王  https://www.cnblogs.com/alex3714/articles/7131523.html Django rest framework介绍 Django REST ...

  3. pycharm eslint should be on a new line

    修改前: "vue/max-attributes-per-line": [2, { "singleline": 10, "multiline" ...

  4. 从前后端的角度分析options预检请求

    摘要:options预检请求是干嘛的?options请求一定会在post请求之前发送吗?前端或者后端开发需要手动干预这个预检请求吗?不用文档定义堆砌名词,从前后端角度单独分析,大白话带你了解! 本文分 ...

  5. 简单记录一下从网上找到的python的渗透方面的第三方库

    python的hacker三方库 Scapy, Scapy3k:发送,嗅探和剖析并伪造网络数据包,可以做交互式应用或单纯的作为库来使用 pypcap, Pcapy and pylibpcap:几个不同 ...

  6. 让ChatGPT帮我写SQL

    推荐一个Github上Start超过3.4K,可将自然语言转化为SQL语句的开源项目. 项目简介 这是一个利用ChatGPT,SQL与自然语言的翻译器.可以将自然语言转换为SQL语句,同样也可以把SQ ...

  7. Net 如何获取私有属性

    .Net的私有属性.成员变量.方法,都可以通过反射获取调用,当然正常我们不会这么操作 此章只是做一个反射科普,像EFCore从数据库取值的底层框架就是通过反射直接操作私有的成员变量,而不是方法. 直接 ...

  8. kafka消费者那些事儿

    前言 消息的消费一般有两种模式,推模式和拉模式.推模式是服务端主动将消息推送给消费者,而拉模式是消费者主动向服务端发起请求来拉取消息.kakfa采用的是拉模式,这样可以很好的控制消费速率.那么kafk ...

  9. OCR -- 文本识别 -- 实践篇

    OCR -- 文本识别 -- 理论篇 本章将详细介绍如何基于PaddleOCR完成CRNN文本识别模型的搭建.训练.评估和预测.数据集采用 icdar 2015,其中训练集有4468张,测试集有207 ...

  10. 网站开发[1] - Spring Boot 快速建立项目

    前言 学校的数据库课程要求做出前端页面对数据库进行交互, 可以使用 Python 或者 Java 语言作为后端, Python语言使用起来非常方便, 但出于对自己的挑战以及更加贴合实际企业开发, 我选 ...