多线程 札记(基础详解,新手入门宝典,源码分析)

一:什么是线程?

线程(英语:thread)是操作系统能够进行运算调度的最小单位
它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,
一个进程中可以并发多个线程,每条线程并行执行不同的任务。

二:创建多线程方式的五种方式:

1、继承Thread类

  • 1:创建一个Thread类的子类
  • 2:在子类中重写run方法
  • 3:创建子类对象
  • 4:通过子类对象调用start方法
  • 5:在main方法里面写一个与之比较
public class ThreadDemo1 extends Thread{ 			//1:创建一个Thread类的子类
	public void run() { 							//2:在子类中重写run方法
		for (int i = 0; i < 5; i++) {
			System.out.println("重写的run方法:"+i);
		}
	}
	public static void main(String[] args) {
		ThreadDemo1 t1 = new ThreadDemo1(); 		//3:创建子类对象
		t1.start();									//4:通过子类对象调用start方法
		for (int i = 0; i<5; i++) {
			System.out.println("main线程:"+i);		//5:在main方法里面写一个与之比较
		}
	}
}

运行结果:
在这里插入图片描述

2、实现Runnable接口

  • java.lang.Runnable
    • Runnable 接口应该由那些打算通过某一线程执行其实例的类来实现。
    • 类必须定义一个称为 run 的无参数方法。
  • java.lang.Thread类的构造方法
    • Thread(Runnable target) 分配新的 Thread 对象。
实现步骤:
  • 1:创建一个Runnable接口的实现类
  • 2:在实现类中重写Runnable接口中的run()方法,设置线程任务
  • 3:创建一个Runnable接口实现类的对象
  • 4:创建Thread对象,调用Thread类的构造器Thread(Runnable target) ,将实现类的对象作为参数
  • 5:调用Thread类中的start方法,开启新的线程执行run()方法
public class ThreadDemo3 {

class RunnableImp implements Runnable{				//1:创建一个Runnable接口的实现类
	@Override
	public void run() {								//2:在实现类中重写Runnable接口中的run()方法,设置线程任务
		for (int i = 0; i < 5; i++) {
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
	}

	public static void main(String[] args) {
		RunnableImp demo3 = new  RunnableImp();		//3:创建一个Runnable接口实现类的对象
		Thread th = new Thread(demo3);				//4:创建Thread对象,调用Thread类的构造器Thread(Runnable target) ,将实现类的对象作为参数
		th.start();									//5:调用Thread类中的start方法,开启新的线程执行run()方法
		for (int i = 0; i <5; i++) {
			System.out.println(Thread.currentThread().getName()+"-->"+i);
		}
	}
}

}

运行效果:
在这里插入图片描述

3、实现callable接口 jdk5.0之后

与runnable接口的比较优势?
  • ①:call()方法是有返回值的
  • ②:call()方法是可以抛出异常的,被外面的操作捕获,获得异常的信息
  • ③:call()方法支持泛型
步骤:
  • 1:创建一个实现callable的实现类
  • 2:实现(重写) call方法,将此线程需要执行的操作声名在call()中
  • 3:创建callable接口实现类的对象
  • 4:将此callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask对象
  • 5:将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
  • 6:获取callable中call方法的返回值
//1:创建一个实现callable的实现类
class NumThread implements 	Callable{
	//2:实现(重写) call方法,将此线程需要执行的操作声名在call()中
	public Object call() throws Exception{
		int sum = 0;
		for(int i=1;i<=100;i++) {
			if(i%2==0) {
				sum +=i;
			}
		}
		return sum;
	}
}

public class ThreadNew {
	public static void main(String[] args) {
		//3:创建callable接口实现类的对象
		NumThread  n = new NumThread();
		//4:将此callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask对象
		FutureTask futuretask = new FutureTask(n);
		//5:将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()		
		new Thread(futuretask).start();
		
		try {
			//get()返回值即为FutureTask构造器里面参数实现callable类重写call()的返回值
			//6:获取callable中call方法的返回值			
			Object sum  = futuretask.get();
			System.out.println(sum);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
		
	}
}

拓展一下:

在这里插入图片描述
我们点进源码探究一下…
发现他传给 thread 类的是这个构造器方法,咦~ 不是说 FutureTask 吗?怎么是 Runnable
在这里插入图片描述
其实这时候我们心里应该有感觉 这个 FutureTask 应该是 runnable的子类了…
在这里插入图片描述
在这里插入图片描述
结论:FutureTask implements RunnableFuture extends Runnable

4、使用匿名内部类

  • 匿名:没有名字
  • 内部类:写在其他类内部的类
匿名内部类的作用:简化代码
  • 把子类继承父类,重写父类方法,创建子类对象 一步完成
  • 把实现类 实现接口,重写接口中的方法,创建实现类的对象 一步完成
匿名内部类的最终产物: 子类/实现类对象,而这个类没有名字
格式:
new 父类/接口(){
	重写父类/接口的方法
};

测试代码

//把子类继承父类(Thread),重写父类方法,创建子类对象
		new Thread() {
			//重写run()方法,设置线程任务
			public void run() {
				for (int i = 0; i < 5; i++) {
					try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+":"+i);
				}
			}
		}.start();
//把实现类 实现接口(Runnable),重写接口中的方法,创建实现类的对象
		new Thread(new Runnable() {
			//重写接口中的方法,设置线程任务
					public void run() {
						for (int i = 0; i < 5; i++) {
							try {
								Thread.sleep(500);
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
							System.out.println(Thread.currentThread().getName()+":"+i);
						}
					}
				}
				).start();

运行结果:
在这里插入图片描述

5、使用线程池 jdk1.5之后

java.util.concurrent包下 类 Executors -->public class Executors 线程池的工具类,用来生成线程池

Executors类中的静态方法
* static ExecutorService newFixedThreadPool(int nThreads)
* 创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。
* 参数:int nThreads -->创建线程池中包含线程的数量
* 返回值:ExecutorService接口,返回的是ExecutorService接口的实现类对象,我们可以使用 ExecutorService接口接收(面向接口编程)

java.util.concurrent包下 接口 ExecutorService -->线程池接口

  • 用来从线程池中提取线程,调用start方法,执行线程任务
    • submit(Runable task) 提交一个 Runnable任务用于执行
  • 关闭,销毁线程池的方法
    • void shutdown()
线程池的使用步骤:
  • 1:使用线程池的工厂类Executors里边提供 的静态方法 newFixedThreadPool 生产一个指定线程数量的线程池
  • 2:创建一个类,实现Runnable接口或Callable接口,重写run方法,设置线程任务
  • 3:调用ExecutorService接口的submit(),传递线程任务(实现类),开启线程,执行run方法
  • 4:调用ExecutorService接口的shutdown()销毁线程池(一般不建议执行)
常用参数:
  • corePoolSize:核心池的大小
  • maximumPoolSize:最大线程数
  • keepAliveTime:线程没有任务时最多保持多长时间后会终止
好处:
  • 1:提高响应速度(减少了创建新线程的时间)
  • 2:降低资源消耗(重复利用线程池中线程,不需要每次都创建)
  • 3:便于线程管理

代码:

public class ThreadDemo10_ThreadPool {
	public static void main(String[] args) {
//1:使用线程池的工厂类Executors里边提供的静态方法 newFixedThreadPool 生产一个指定线程数量的线程池
		ExecutorService pool = Executors.newFixedThreadPool(2);//返回一个接口
//3:调用ExecutorService接口的submit(),传递线程任务(实现类),开启线程,执行run方法
		/*Runnable10 run = new Runnable10();
		pool.submit(run);*/
		//线程池会一直开启,使用完了线程,会自动把线程归还给线程池,线程可以继续使用
		pool.submit(new Runnable10());
		pool.submit(new Runnable10());
		pool.submit(new Runnable10());
		pool.submit(new Runnable10());
		pool.submit(new Runnable10());
//4:调用ExecutorService接口的shutdown()销毁线程池(一般不建议执行)
//		pool.shutdown();
	}
}
//2:创建一个类,实现Runnable接口,重写run方法,设置线程任务
class Runnable10 implements Runnable{

	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName()+" 创建了一个新线程");
	}
	
}
拓展一下:

在这里插入图片描述
为什么execute()方法 适合runnable呢?
看源码~
在这里插入图片描述
为什么submit()适合callable呢?
看源码~

关于线程创建的五种方式图解

请添加图片描述

三:多线程的安全问题

线程安全问题引入

三个窗口卖出同一张票的事故 以及卖出不存在的票

卖票案例

创建3个线程,同时开启,对共享的票进行出售

public class ThreadDemo5_Safe {
	public static void main(String[] args) {
		//创建Runnable接口的实现类对象
		Runnable impl = new RunnableImpl();
		//创建Thread对象,构造方法中传递Runnable接口的实现类对象
		Thread t1 = new Thread(impl);
		Thread t2 = new Thread(impl);
		Thread t3 = new Thread(impl);
		t1.start();
		t2.start();
		t3.start();
	}
}
class RunnableImpl implements Runnable{
	//定义 一个共享的数据:票
	private int ticket=100;
	//设置线程任务:卖票
	@Override
	public void run() {
		//使用循环卖票 判断是否有票
		while(ticket>0) {
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"卖:"+ticket);
			ticket--;
		}
	}
	
}

问题漏洞:
在这里插入图片描述
在这里插入图片描述

如何解决线程安全问题? 有三种方式

1:同步代码块

格式

	synchronized(锁对象) {
		放置可能会出现线程安全问题的代码(访问了共享数据的代码)
	}

注意:

  • 1:通过代码块中的锁对象,可以使用任意的对象
  • 2:必须保证多个线程使用同一个锁对象
  • 3:锁对象的作用:把同步代码块锁住,只让一个线程在同步代码块中执行

代码:

//实现Runnable接口
class RunnableImpl6 implements Runnable{
	//定义票的总数
	private int ticket = 100;
	//定义一个锁
	Object obj = new Object();
	
	//重写run()方法
	@Override
	public void run() {
		while(ticket>0) {
			//同步代码块 每次只能让一个线程进来 所以后面还需要加上一个判断票数
			synchronized(obj) {
				//需判断票是否有剩
				if(ticket>0) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+":"+ticket);
					ticket--;
				}
			}
		}
	}
	
}
public class ThreadDemo6_SolveSafe {
	public static void main(String[] args) {
		//创建接口类的实现类的对象
		RunnableImpl6 impl = new RunnableImpl6();
		//创建Thread的对象
		Thread t1 = new Thread(impl);
		Thread t2 = new Thread(impl);
		Thread t3 = new Thread(impl);
		t1.start();
		t2.start();
		t3.start();
	}
}

运行结果:
在这里插入图片描述

2:同步方法

格式

	修饰符 synchronized 返回值类型 方法名(参数列表){
			有关线程安全问题的代码(访问了共享数据的代码)
		}

使用步骤:

  • 1:把访问了共享数据的代码抽取出来 放进一个方法里面
  • 2:在方法上添加synchronized修饰符

写好同步方法之后,在实现类重写run()方法里面调用即可
代码:

class Runnable7 implements Runnable{
	private int ticket = 100;
	@Override
	public void run() {
		while(ticket>0) {
			TongBuMethod();
		}
	}
	/*定义一个同步方法
	 * 同步方法也会把方法内部的代码锁住,只让一个线程进行
	 * 同步方法的锁对象就是实现类对象 new Runnable7() 也就是this
	 * */
	public synchronized void TongBuMethod() {
		if(ticket>0) {
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+":"+ticket);
			ticket--;
		}
	}
}
public class ThreadDemo7_SolveSafe {
	public static void main(String[] args) {
		Runnable7 impl = new Runnable7();
		Thread t1 = new Thread(impl);
		Thread t2 = new Thread(impl);
		Thread t3 = new Thread(impl);
		t1.start();
		t2.start();
		t3.start();
	}
}
/*定义静态同步方法
	 * 锁对象就不是this了,this是创建对象之后产生的,静态方法优先于对象
	 * 静态方法的锁对象是本类的class属性 -->	class文件对象
	 * */
	public static synchronized void StaticTongBuMethod() {
		if(ticket>0) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+":"+ticket);
			ticket --;
		}
	}
}
关于同步方法的总结:
  • 1:同步方法仍然涉及到同步监视器,只是不需要我们显式的声名
  • 2:非静态的同步方法,同步监视器是this
  • 3:静态的同步方法,同步监视器是 当前类本身
  • 其实就是看锁是否唯一

3;Lock锁机制 jdk5.0之后

异同:
  • 相同:都能解决线程的安全问题
  • 不同:synchronized 自动释放同步监视器、lock需手动启动同步与结束同步
  • Lock是显式锁(手动开启,手动关闭) 只有代码块锁
  • synchronized是隐式锁,出了作用域自动释放 有代码块锁和方法锁
优先原则:
  • 使用lock锁,JVM将花费较少时间来调度线程,性能更好
  • 优先使用顺序:lock ——> 同步代码块 ——>同步方法

java.util.concurrent.locks.Lock接口

  • Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作
  • 使用Lock接口中的两个方法:lock()和unlock()

注意:既然Lock是个接口 就需要用到他的实现类:ReentrantLock

  • java.util.concurrent.locks.ReentrantLock

使用步骤:

  • 1:在成员变量位置创建一个ReentrantLock对象
  • 2;在可能出现线程安全问题的代码前 调用Lock接口中的 Lock()获取锁
  • 3:在可能出现线程安全问题的代码后 调用Lock接口中的 unLock()释放锁

代码:

public class ThreadDemo8_SolveSafe {
	public static void main(String[] args) {
		Runnable8 r = new Runnable8();
		Thread t1 = new Thread(r);
		Thread t2 = new Thread(r);
		Thread t3 = new Thread(r);
		t1.start();
		t2.start();
		t3.start();
	}
}
class Runnable8 implements Runnable{
	public int ticket = 100;
//	1:在成员变量位置创建一个ReentrantLock对象
	ReentrantLock lock = new ReentrantLock();
	@Override
	public void run() {
		while(ticket>0) {
//			2;在可能出现线程安全问题的代码前 调用Lock接口中的 Lock()获取锁
			lock.lock();
			if(ticket>0) {
				try {
					Thread.sleep(100);
					System.out.println(Thread.currentThread().getName()+":"+ticket);
					ticket--;
				} catch (InterruptedException e) {
					e.printStackTrace();
				}finally {
//					3:在可能出现线程安全问题的代码后 调用Lock接口中的 unLock()释放锁
					lock.unlock();
				}
				//最好把释放锁的代码 放在异常捕获的finally语句中,那么无论是否发生异常,锁都会进行释放
			}
		}
}
}

解决线程安全问题的三种方式

请添加图片描述

四:线程的死锁问题

关于死锁的理解:

  • 不同的线程分别占用对方需要的同步资源不放弃
  • 都在等待对方放弃自己需要的同步资源,就形成了线程的死锁

死锁现象说明:

  • 出现死锁后,不会出现异常,不会 出现提示,只是所有的线程都处于阻塞状态,无法继续
  • 我们使用同步时,要避免使用死锁

代码:

public class DeadLock {
	public static void main(String[] args) {
		StringBuffer s1 = new StringBuffer();
		StringBuffer s2 = new StringBuffer();
//		匿名内部类 继承Thread
		new Thread() {
			public void run() {
				synchronized(s1) {
					s1.append("a");
					s2.append("1");
					
					try {
						Thread.sleep(100);
					}catch(InterruptedException e) {
						e.printStackTrace();
					}
					
					synchronized(s2) {
						s1.append("b");
						s2.append("2");
						
						System.out.println(s1);
						System.out.println(s2);
					}
				}
			}
		}.start();
		
		//匿名内部类 实现runnable接口
		new Thread(new Runnable() {
			public void run() {
				
				synchronized(s2) {
					s1.append("c");
					s2.append("3");
					
					try {
						Thread.sleep(100);
					}catch(InterruptedException e) {
						e.printStackTrace();
					}
					
					synchronized(s1) {
						s1.append("d");
						s2.append("4");
						
						System.out.println(s1);
						System.out.println(s2);
					}
				}
				
				
			}
		}) .start();
}
}

运行结果:
在这里插入图片描述

线程死锁图解

请添加图片描述

五:线程通信

关于线程通信的三个方法:

  • wait():一旦执行此方法,当前线程进入阻塞状态,并释放同步监视器
  • notify():一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程被wait,则唤醒优先级最高的那个。
  • notifyAll():一旦执行此方法,就会唤醒所有被wait的线程

注意点:

  • wait(),notify(),notifyAll()是三个线程通信的方法
  • 使用这三个方法的前提:只能出现在同步代码块 或者 同步方法
  • wait(),notify(),notifyAll()三个方法的调用者 必须是同步代码块或同步方法中的同步监视器(锁)
  • 否则,会报错IllegalMonitorStateException异常

小扩展:

  • wait(),notify(),notifyAll()三个方法是定义在java.lang.Object类中
  • 因为所有对象都能作为同步监视器,而同步监视器能调用这三个方法,所以这三个方法属于java.lang.Object类、

代码:

class Number implements Runnable{
	public int num =1;
	public Object obj = new Object();
	public  void run() {
		while(true) {
			//同步代码块 同步监视器为this -> Number		
			//可将this 改为obj 那么下面就得使用obj调用notify()和wait()方法
			synchronized(this) {
				//把当前wait的线程唤醒 虽然唤醒,
				//但是锁已被占用,即使唤醒也无法进来
				//此时省略的是this.(非静态方法)		如果是静态方法,则默认是调用Number.
				notify();
				if(num<=10) {
					System.out.println(Thread.currentThread().getName()+":"+num);
					try {
						Thread.sleep(300);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					num++;
					try {
						//使调用wait()方法的进程进入阻塞状态 释放锁
						wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}else {
					break;
				}
			}
			
		}
	}
}
public class CommunicateTest {
	public static void main(String[] args) {
		Number  num = new Number();
		Thread t1 = new Thread(num);
		Thread t2 = new Thread(num);
		t1.setName("线程一");
		t2.setName("线程二");
		t1.start();
		t2.start();
	}
	
}

运行结果:
在这里插入图片描述

进入到TimeWaiting(计时等待) 有两种方式

  • sleep(long timeout)
    • 在毫秒值结束之后,线程睡醒进入到Runnable/Blocked状态
  • void wait(long timeout)
    • wait方法如果在毫秒值结束之后,还没有被notify唤醒,就会自动醒来,线程睡醒进入到Runnable/Blocked状态
    • 类似sleep方法

唤醒方法有两个

  • notify()唤醒在此对象监视器上等待的单个线程
  • notifyAll()唤醒在此对象监视器上等待的全部线程

sleep()和wait()的异同

  • 相同点:一旦执行此方法,都可以使得当前线程进入阻塞状态
  • 不同点:
    • 两个方法声名的位置不同: Thread类中声名sleep(),Object类中声名wait()
    • 调用的要求不同,sleep可以在任何场景使用, wait必须在同步代码块或同步方法中使用
    • 关于释放同步监视器:如果两个方法都在同步代码块或同步方法中
      • sleep不释放同步监视器
      • wait释放同步监视器

等待唤醒案例:线程之间的通信

  • 创建一个消费者:我手机爆屏了,快帮我修理!
    • 然后调用wait方法,放弃对CPU的争抢,进入到waiting状态(无限等待)
  • 创建一个生产者:花3秒修理屏幕
    • 修理好之后 ,调用notify方法,唤醒顾客拿手机

注意:

  • 此时顾客和店家必须用同步代码块包起来,保证等待和唤醒只能有一个在执行
  • 同步使用的锁对象必须保证唯一
  • 只有锁对象才能调用wait和notify方法(就是Object类中的方法)
public class ThreadDemo9_WaitAndNotify {
	public static void main(String[] args) {
		//创建锁对象
		Object obj  = new Object();
		//创建顾客线程 使用匿名内部类
		 new Thread() {
		 public void run() {
				while(true) {
					//保证等待和唤醒只能有一个在执行 使用同步代码块
					synchronized(obj){
						System.out.println("我手机爆屏了,快帮我修理!");
						//调用wait方法,放弃对CPU的争抢,进入到waiting状态(无限等待)
						try {
							obj.wait();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						//被唤醒后会继续执行wait之后的语句
						System.out.println("谢谢老板,老板身体健康!");
					}
				}
			}
		 }.start();
		 
		 //创建店家线程 使用匿名内部类
		 new Thread() {
			 public void run() {
				 while(true) {
					 //先睡三秒 放弃对CPU的争夺 让顾客先抢到
					 try {
						 Thread.sleep(3000);
					 } catch (InterruptedException e) {
						 e.printStackTrace();
					 }
					synchronized(obj) {
						//修理好之后 ,调用notify方法,唤醒顾客拿手机
						 System.out.println("花3秒修理屏幕... 你过来拿手机");
						 obj.notify();
					}
				 }
			 }
		 }.start();
	}
}

运行结果:
在这里插入图片描述
再次感受一下 notify()和notifyAll()的区别~
两个顾客 一个店家举例

public class ThreadDemo9$5 {
	public static void main(String[] args) {
		//创建锁对象
				Object obj  = new Object();
				//创建顾客线程 使用匿名内部类
				 new Thread() {
				 public void run() {
						while(true) {
							//保证等待和唤醒只能有一个在执行 使用同步代码块
							synchronized(obj){
								System.out.println("我手机爆屏了,快帮我修理!");
								//调用wait方法,放弃对CPU的争抢,进入到waiting状态(无限等待)
								try {
									obj.wait();
								} catch (InterruptedException e) {
									e.printStackTrace();
								}
								//被唤醒后会继续执行wait之后的语句
								System.out.println("手机修好了~谢谢老板,老板身体健康!");
							}
						}
					}
				 }.start();
				//创建顾客线程 使用匿名内部类
				 new Thread() {
				 public void run() {
						while(true) {
							//保证等待和唤醒只能有一个在执行 使用同步代码块
							synchronized(obj){
								System.out.println("我笔记本爆屏了,快帮我修理!");
								//调用wait方法,放弃对CPU的争抢,进入到waiting状态(无限等待)
								try {
									obj.wait();
								} catch (InterruptedException e) {
									e.printStackTrace();
								}
								//被唤醒后会继续执行wait之后的语句
								System.out.println("笔记本修好了~恭喜发财,老板万事如意!");
							}
						}
					}
				 }.start();;
				 //创建店家线程 使用匿名内部类
				 new Thread() {
					 public void run() {
						 while(true) {
							 //先睡三秒 放弃对CPU的争夺 让顾客先抢到
							 try {
								 Thread.sleep(3000);
							 } catch (InterruptedException e) {
								 e.printStackTrace();
							 }
							synchronized(obj) {
								//修理好之后 ,调用notify方法,唤醒顾客拿手机
								 System.out.println("花3秒修理屏幕... 你过来拿");
								  obj.notify(); //唤醒一个
//								  obj.notifyAll();
							}
						 }
					 }
				 }.start();
	}
}

运行结果:
在这里插入图片描述
在这里插入图片描述

线程通信图解

请添加图片描述

六:线程状态(6种)

在这里插入图片描述
阻塞状态:具有CPU的执行资格,等待CPU空闲时执行
休眠状态:放弃CPU的执行资格,CPU空闲,也不执行

线程的状态图解

请添加图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值