线程
1 线程概念
进程:进程指的是正在运行的程序。确切来说,当一个程序进入到内存中运行,就变成一个进程。进程是处于运行过程中的程序,并且具有一定的独立功能。
线程:线程是进程中的一个执行单元,负责当前进程中程序的执行。一个进程中至少要有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
2 抢占式调度
抢占式调度就是优先级高的线程抢占到CPU资源的概率更大。如果优先级相同,那么会随机选择一个线程执行,这种就是抢占式调度。Java采用的就是抢占式调度。
CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核来说,某一个时刻,只能执行一个线程。CPU在多个线程间切换的速度相对于人类来说非常快,看上去好像是同时在运行。
3 多线程优点
- 提高了用户体验
- 多线程程序并不能提高程序的运行速度,但是能够提高程序的运行效率,让CPU的利用率更高。
- 计算机执行任务是在CPU进行的。
- 线程在执行过程中会和计算机硬件进行交互。线程在和计算机硬件交互的时候会暂时空置CPU。
- 一个线程利用率假设是80%,两个线程利用率提高到96%
4 主线程
JVM启动后,必然有一个执行路径(线程)从main方法开始,一直执行到main方法结束。这个线程在java中称之为主线程(main线程)。当程序在主线程中遇到了循环而导致程序在执行的位置停留时间过长,则无法马上执行循环下面的程序。需要等待循环结束后才能够执行.我们可以再手动开辟一条新的线程去执行耗时的一些代码。
5 线程的定义方式
5.1 定义线程的方式一
java中的线程的顶级父类是Thread
创建线程的步骤:
- 定义一个类继承Thread类
- 重写Thread类中的run方法,把需要新的线程执行的代码放入run方法中
- 创建子类对象
- 通过子类对象调用start方法。start方法会开启新的线程去执行run方法
定义类继承Thread类,重写run方法
public class MyThread extends Thread{
@Override
public void run() {
// 把需要新的线程执行的代码放入run方法中
for (int i = 0; i < 100; i++) {
System.out.println("新的线程在执行" + i);
}
}
}
创建子类对象使用
public static void main(String[] args) {
// 创建子类对象
MyThread myThread = new MyThread();
// 开启新线程执行代码
myThread.start();
for (int i = 0; i < 100; i++) {
System.out.println("主线程在执行" + i);
}
}
5.2 定义线程的方式二(重点)
- 定义类实现Runnable接口
- 重写Runnable接口的run方法
- 创建Thread类对象
- 将Runnable接口的子类对象作为参数传递给Thread类的构造方法
- 调用Thread类的start方法开启线程
定义类实现Runnable接口
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
// Thread.currentThread() 获取当前线程
// getName() 获取线程名
System.out.println(Thread.currentThread().getName() + "正在执行" + i);
}
}
}
开启线程执行代码
public class TestDemo2 {
public static void main(String[] args) {
// 创建Runnable接口的实现类
// Runnable runnable = new MyRunnable();
// // 将实现类对象通过参数传递给Thread类的构造方法
// // 参数二:给线程设置名称
// Thread thread = new Thread(runnable,"新线程");
// // 调用start方法 开启线程执行run方法
// thread.start();
// 开启线程执行 匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "正在执行");
}
}
}).start();
// lambda表达式
new Thread(() -> {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "正在执行");
}
},"新的线程").start();
for (int i = 0; i < 10000; i++) {
System.out.println(Thread.currentThread().getName() + "正在执行...");
}
}
}
课堂案例:售票案例
模拟电影院的卖票过程。假设"奥本海默"电影座位总共有100个。来模拟电影院的售票窗口,实现3个窗口同时卖电影票。票卖完为止。
实现方式一:
public class Ticket implements Runnable{
int ticket = 100;
@Override
public void run() {
while (true){
if (ticket <= 0){
break;
}
System.out.println(Thread.currentThread().getName() + ",您的座位是" + ticket--);
}
}
}
测试
// 创建票对象
Ticket ticket = new Ticket();
// 创建3个窗口
Thread thread1 = new Thread(ticket,"窗口1");
Thread thread2 = new Thread(ticket,"窗口2");
Thread thread3 = new Thread(ticket,"窗口3");
// 开启线程去售票
thread1.start();
thread2.start();
thread3.start();
实现方式二:
public class TicketDemo {
// 票是多个线程共享的数据
static int ticket = 100;
public static void saleTicket(){
// 卖票
while (true){
if (ticket <= 0){
break;
}
System.out.println(Thread.currentThread().getName() + ",您的座位是" + ticket--);
}
}
}
// Thread thread1 = new Thread(() -> TicketDemo.saleTicket(),"窗口1");
// Thread thread2 = new Thread(() -> TicketDemo.saleTicket(),"窗口2");
// Thread thread3 = new Thread(() -> TicketDemo.saleTicket(),"窗口3");
Thread thread1 = new Thread(TicketDemo::saleTicket,"窗口1");
Thread thread2 = new Thread(TicketDemo::saleTicket,"窗口2");
Thread thread3 = new Thread(TicketDemo::saleTicket,"窗口3");
thread1.start();
thread2.start();
thread3.start();
当多次运行时,会发现结果会出现重复的票,和0或者-1的票
如果有多个线程在同时运行,而这些线程可能会同时运行某段代码。程序每次运行的结果如果和单线程的运行结果保持一致,而且其他变量的值也和预期的一样,就是线程安全。
如果不一样,就是有线程安全隐患。
总结:出现线程安全隐患的条件
- 多条线程
- 共享资源
- 有修改(更新 删除 添加)操作
5.3 定义线程的方式三
使用Callable接口和Future。类不是一个线程类型,仅仅是Callable接口的实现类。
Callable接口对比Runnable接口
Callable是一个泛型接口,Runnable仅仅是一个普通接口
Callable的方法是call方法,Runnable接口中是run方法
call方法有返回值类型,并且处理了一切异常,有一个回调的结果,但线程逻辑执行完毕后,会返回一个数据。
run方法无参无返回值
需求:模拟多个账户对同一个账户进行转账
对张三账户操作 账户余额是2000
李四对张三转账1000
王五对张三转账2000
张三媳妇取钱1000
实现方式一:
package cn.javasm.threaddemo;
import java.math.BigDecimal;
import java.util.concurrent.Callable;
/***
* @className: AccountDemo
* @author: gfs
* @description:
* @date: 2024/7/1 15:58
* @version: 0.1
* @since :jdk11
*/
public class AccountDemo implements Callable<BigDecimal> {
// 张三的账户余额
private static BigDecimal balance = new BigDecimal("2000");
// 转账或取钱的金额
private BigDecimal money;
// true 取钱 false转账
private boolean flag = false;
public AccountDemo(BigDecimal money, boolean flag) {
this.money = money;
this.flag = flag;
}
@Override
public BigDecimal call() throws Exception {
if (!flag){
// 转账
System.out.println(Thread.currentThread().getName() + "转账:" + money);
balance = balance.add(money);
}else {
if (money.compareTo(balance) > 0){
throw new RuntimeException("余额不足,取钱失败");
}
balance = balance.subtract(money);
System.out.println(Thread.currentThread().getName() + "取钱:" + money);
}
return balance;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
BigDecimal money1 = new BigDecimal("1000");
BigDecimal money2 = new BigDecimal("2000");
AccountDemo lisiZhuan = new AccountDemo(money1,false);
AccountDemo wangwuZhuan = new AccountDemo(money2,false);
AccountDemo wifeQu = new AccountDemo(money1,true);
FutureTask<BigDecimal> task1 = new FutureTask<>(lisiZhuan);
FutureTask<BigDecimal> task2 = new FutureTask<>(wangwuZhuan);
FutureTask<BigDecimal> task3 = new FutureTask<>(wifeQu);
Thread t1 = new Thread(task1, "李四");
Thread t2 = new Thread(task2, "王五");
Thread t3 = new Thread(task3, "媳妇");
// 开启线程执行
t1.start();
t2.start();
t3.start();
}
实现方式二:
Account类
package cn.javasm.threaddemo;
import java.math.BigDecimal;
/***
* @className: Account
* @author: gfs
* @description:
* @date: 2024/7/1 16:19
* @version: 0.1
* @since :jdk11
*/
public class Account {
private static BigDecimal balance = new BigDecimal("2000");
// 存钱
public static BigDecimal cunMoney(BigDecimal money){
if (money == null || money.doubleValue() < 0){
throw new NullPointerException("数据不正确");
}
System.out.println(Thread.currentThread().getName() + "转账:" + money);
balance = balance.add(money);
return balance;
}
// 取钱
public static BigDecimal quMoney(BigDecimal money){
if (money == null || money.doubleValue() < 0){
throw new NullPointerException("数据不正确");
}
if (money.compareTo(balance) > 0){
throw new RuntimeException("余额不足,取钱失败");
}
balance = balance.subtract(money);
System.out.println(Thread.currentThread().getName() + "取钱:" + money);
return balance;
}
}
测试
public static void main(String[] args) {
BigDecimal money1 = new BigDecimal("1000");
BigDecimal money2 = new BigDecimal("2000");
FutureTask<BigDecimal> task1 = new FutureTask<>(() -> Account.cunMoney(money1));
FutureTask<BigDecimal> task2 = new FutureTask<>(() -> Account.cunMoney(money2));
FutureTask<BigDecimal> task3 = new FutureTask<>(() -> Account.quMoney(money1));
Thread t1 = new Thread(task1, "李四");
Thread t2 = new Thread(task2, "王五");
Thread t3 = new Thread(task3, "媳妇");
// 开启线程执行
t1.start();
t2.start();
t3.start();
}
6 线程安全隐患解决方案
只要破坏线程安全隐患出现的三个条件,就可以解决线程安全隐患。
6.1 同步代码块
同步代码块能够保证在{}中同一时刻最多只有一个线程在执行。线程执行完之后其他线程再去执行。
格式:
synchronized(锁对象){
可能会产生线程安全隐患的代码
}
同步代码块中的锁对象可以是任意的对象。但是有多个线程时,要使用同一个锁对象才能保证线程安全。
方法区中的所有内容都被线程共享的。所以方法区的内容都可以作为锁对象。
public class TicketDemo {
// 票是多个线程共享的数据
static int ticket = 100;
// 锁对象一定要保证多个锁对象是共享的
// static Object obj = new Object();
public static void saleTicket() {
// 卖票
while (true) {
try {
// 让当前线程休眠1毫秒
Thread.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized ("a"){// 自动加锁
if (ticket <= 0) {
break;
}
System.out.println(Thread.currentThread().getName() + ",您的座位是" + ticket--);
}// 自动释放锁
}
}
}
6.2 同步方法
同步方法能够保证在方法中,同一时刻只有一条线程在执行。
格式:
public synchronized void method(){
可能会产生线程安全问题的代码;
}
// 同步方法的锁对象是this
private synchronized boolean method() {
if (ticket <= 0){
return true;
}
System.out.println(Thread.currentThread().getName() + ",您的座位是" + ticket--);
return false;
}
6.3 静态同步方法
同步方法能够保证在方法中,同一时刻只有一条线程在执行。
格式:
public synchronized static void method(){
可能会产生线程安全问题的代码;
}
// 静态同步方法 静态同步方法的锁对象 类名.class
private synchronized static boolean method() {
if (ticket <= 0) {
return true;
}
System.out.println(Thread.currentThread().getName() + ",您的座位是" + ticket--);
return false;
}
6.4 Lock
Lock等价于synchornized,需要手动上锁和释放锁。
static Lock lock = new ReentrantLock();
public static void saleTicket() {
// 卖票
while (true) {
try {
// 让当前线程休眠1毫秒
Thread.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// if (method()) break;
// 手动加锁
lock.lock();
try {
if (ticket <= 0) break;
System.out.println(Thread.currentThread().getName() + ",您的座位是" + ticket--);
}finally {
// 手动释放锁
lock.unlock();
}
}
}
同步:一段代码在同一个时刻只允许一个线程执行
异步:一段代码在同一个时刻允许多个线程执行
同步一定是线程安全
线程安全不一定同步
异步不一定线程不安全
线程不安全一定是异步
HashMap是一个异步线程不安全的映射
Hashtable是一个同步线程安全的映射
ConcurrentHashMap 是一个异步线程安全的映射
7 死锁
死锁:由于锁的嵌套导致锁之间相互锁死的现象
package cn.javasm.threaddemo;
/***
* @className: TestDemo6
* @author: gfs
* @description:
* @date: 2024/7/1 16:58
* @version: 0.1
* @since :jdk11
*/
public class TestDemo6 {
static Printer printer = new Printer();
static Scan scan = new Scan();
public static void main(String[] args) {
// 开线程使用打印机和扫描仪
new Thread(()->{
// 先用打印机再用扫描仪
synchronized (printer){
printer.print();
try {
// 模拟使用打印机一段时间
Thread.sleep(1000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 打印之后马上扫描
synchronized (scan){
scan.scan();
}
}
}).start();
new Thread(() -> {
// 先用扫描仪再用打印机
synchronized (scan){
scan.scan();
synchronized (printer){
printer.print();
}
}
}).start();
}
}
class Printer{
public void print(){
System.out.println("打印机打印内容");
}
}
class Scan{
public void scan(){
System.out.println("扫描仪扫描内容");
}
}
8 等待唤醒机制
package cn.javasm.threaddemo;
/***
* @className: TestDemo7
* @author: gfs
* @description:
* @date: 2024/7/1 17:15
* @version: 0.1
* @since :jdk11
*/
public class TestDemo7 {
public static void main(String[] args) {
Student student = new Student();
student.setName("白骨精");
student.setGender("女生");
new Thread(new Ask(student)).start();
new Thread(new Change(student)).start();
}
}
// 切换学生的类 开新线程交换学生 实现轮流问问题
class Change implements Runnable{
private Student student;
public Change(Student student) {
this.student = student;
}
@Override
public void run() {
while (true){
synchronized (student){
if (student.flag){
try {
student.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// 切换学生
if ("孙悟空".equals(student.getName())){
student.setName("白骨精");
student.setGender("女生");
}else {
student.setName("孙悟空");
student.setGender("男生");
}
student.flag = true;
// 唤醒正在等待的线程
student.notify();
}
}
}
}
class Ask implements Runnable{
private Student student;
public Ask(Student student) {
this.student = student;
}
@Override
public void run() {
while (true){
synchronized (student){
if (!student.flag){
try {
student.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("老师,我是" + student.getName() + ",我是" + student.getGender() + ",我要问问题");
student.flag = false;
student.notify();
}
}
}
}
class Student{
private String name;
private String gender;
// true 表示Ask线程执行
// false 表示Change线程执行
boolean flag = true;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
}
课堂练习:
一个线程作为消费者,一个线程作为生产者。生产者每生产一次,消费者就消费一次。生产者每次生产的商品数量以及消费者每次消费的数量用随机数产生即可。每一次的生产的商品数量和上一次剩余的商品数量之和不能超过1000.
package cn.javasm.demo;
/***
* @className: Practice
* @author: gfs
* @description:
* @date: 2024/7/2 10:22
* @version: 0.1
* @since :jdk11
*/
public class Practice {
public static void main(String[] args) {
// 一个线程作为消费者,一个线程作为生产者。生产者每生产一次,消费者就消费一次。
// 生产者每次生产的商品数量以及消费者每次消费的数量用随机数产生即可。
// 每一次的生产的商品数量和上一次剩余的商品数量之和不能超过1000.
Product product = new Product();
new Thread(new Consumer(product)).start();
new Thread(new Consumer(product)).start();
new Thread(new Consumer(product)).start();
new Thread(new Producer(product)).start();
new Thread(new Producer(product)).start();
new Thread(new Producer(product)).start();
}
}
class Product{
// 商品的数量
private int count;
// true 生产 false消费
boolean flag = true;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
// 生产者
class Producer implements Runnable{
private Product product;
public Producer(Product product) {
this.product = product;
}
@Override
public void run() {
while (true){
synchronized (product){
// 生产商品
while (!product.flag){
try {
product.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// 计算本次能够生产的最大数量
int max = 1000 - product.getCount();
// 本次生产的实际数量
int count = (int)(Math.random() * (max + 1));
// 计算本次所能提供的商品数量
product.setCount(product.getCount() + count);
System.out.println("本次生产的商品数量是" + count + ",本次提供的商品数量是" + product.getCount());
product.flag = false;
// 欢迎正在等待的线程
product.notifyAll();
}
}
}
}
// 消费者
class Consumer implements Runnable{
private Product product;
public Consumer(Product product) {
this.product = product;
}
@Override
public void run() {
while (true){
synchronized (product){
// 消费商品
while (product.flag){
try {
product.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// 计算本次所消费的商品数量
int count = (int)(Math.random() * (product.getCount() + 1));
// 计算本次剩余的商品数量
product.setCount(product.getCount() - count);
System.out.println("本次消费的数量是" + count + ",本次剩余的商品数量是" + product.getCount());
product.flag = true;
// 欢迎正在等待的线程
product.notifyAll();
}
}
}
}
9 wait和sleep的区别
总结:
sleep需要指定休眠时间,到点自然醒。如果线程没有锁,那么会释放执行权。如果线程有锁,那么不释放执行权。这个方法是设计在Thread类上,是一个静态方法。
wait方法可以指定也可以不指定时间,如果不指定时间需要进行唤醒。释放执行权也释放锁。这个方法是设计在Object上,是一个普通方法。
注意点:等待唤醒机制必须结合锁来使用。
使用的锁对象和等待和唤醒的锁对象应该是同一个对象。
10 线程池
10.1 Executors
private static void demo4() {
// 创建可以执行延时/定时任务的线程池
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
// delay:延迟的数字
// TimeUnit: 延迟的时间单位
System.out.println("当前时间" + LocalDateTime.now());
scheduledExecutorService.schedule(()->{
System.out.println("结束时间" + LocalDateTime.now());
System.out.println("helloworld");
},5, TimeUnit.SECONDS);
}
private static void demo3() {
// 创建一个可以伸缩的线程池对象
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 100; i++) {
executorService.submit(()->{
System.out.println(Thread.currentThread().getName() + "正在执行");
});
}
}
private static void demo2() {
// 创建固定数量的线程对象的线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 20; i++) {
executorService.submit(()->{
System.out.println(Thread.currentThread().getName() + "执行了...");
});
}
}
private static void demo1() {
// 创建只有1个线程实例的线程池 只有1个Thread类对象
ExecutorService executorService = Executors.newSingleThreadExecutor();
// 线程池一旦启动,不会停止
// 不能并发 10个任务 在一个时间1个被执行 其他9个都在等待
for (int i = 0; i < 10; i++) {
executorService.submit(()->{
System.out.println(Thread.currentThread().getName() + "执行了...");
});
}
// 关闭线程池
executorService.shutdown();
// 立即关闭线程池
// executorService.shutdownNow();
}
10.2 ThreadPoolExecutor
private static void demo5() {
// 创建满足不同并发请求量的线程池实力
/**
* corePoolSize: 线程池中初始化的核心线程数量
* int maximumPoolSize: 线程池中允许的最大的线程数量
* long keepAliveTime: 在指定的时间内回收线程 线程池中闲置的线程数量 > corePoolSize
* TimeUnit unit : keepAliveTime的时间单位
* BlockingQueue<Runnable> workQueue: 任务队列控制
* ArrayBlockingQueue: 有界队列 限制排队的最大任务量 常用
* LinkedBlockingQueue: 无界队列 可以排任意数量的任务 会导致内存不足
* PriorityBlockingQueue: 优先队列
* ThreadFactory threadFactory: 创建线程对象
* RejectedExecutionHandler handler:拒绝策略 用户请求300个任务 并发执行100个 排队 100个 剩下100去主线程执行
*/
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(5,100,
10,
TimeUnit.MINUTES,
new ArrayBlockingQueue<>(100),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy()
);
}
10.3 单例设计模式
单例设计模式保证某一个类所创建出来的对象在整个项目中只有一个。
单例设计模式分为懒汉式和饿汉式
饿汉式
class TaskManager{
// 饿汉式
// 静态的变量外面才可以获取,静态才是共享的
// 不希望外面可以直接获取到,而且在获取到单例模式对象之前希望通过方法做一些初始化工作,所以加private
private static TaskManager taskManager = new TaskManager();
// 不允许类外随意创建,否则就会有多个对象
private TaskManager(){
}
public static TaskManager getInstance(){
// 当方法做完一些初始化操作后再返回给外面
return taskManager;
}
public static void m(){
System.out.println("m......");
}
}
懒汉式
class TaskManager2{
// 懒汉式
// 私有的静态变量
private static TaskManager2 taskManager2;
// 私有的构造方法
private TaskManager2(){
}
public static TaskManager2 getInstance(){
// 懒加载 lazyload
// 懒加载就是当首次真正使用到对象的时候再去创建对象
if (taskManager2 == null){
taskManager2 = new TaskManager2();
}
return taskManager2;
}
}
单例模式:
- 在整个项目中只存在唯一的一个实例
- 饿汉式:降低加载效率,线程安全
- 懒汉式:对象在真正使用的时候才去创建,节省加载时间,但是线程不安全,多线程时有可能出现多个对象
懒汉式线程不安全,可以使用同步代码块或者同步方法
public static TaskManager2 getInstance(){
// 懒加载 lazyload
// 懒加载就是当首次真正使用到对象的时候再去创建对象
synchronized (TaskManager2.class){
if (taskManager2 == null){
taskManager2 = new TaskManager2();
}
}
return taskManager2;
}
使用单例模式封装线程池
package cn.javasm.demo;
import java.util.concurrent.*;
/***
* @className: MyThreadPool
* @author: gfs
* @description: 线程池工具类
* @date: 2024/7/2 16:09
* @version: 0.1
* @since :jdk11
*/
public class MyThreadPool {
// 饿汉式
private MyThreadPool(){}
private static final MyThreadPool MY_THREAD_POOL = new MyThreadPool();
private static final ThreadPoolExecutor POOL_EXECUTOR;
private static final ScheduledThreadPoolExecutor SCHEDULED_THREAD_POOL_EXECUTOR;
static {
// 静态代码块进行初始化
POOL_EXECUTOR = new ThreadPoolExecutor(5,100,
10,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy());
SCHEDULED_THREAD_POOL_EXECUTOR = new ScheduledThreadPoolExecutor(10);
}
// 获取单例模式的对象
public static MyThreadPool getInstance(){
return MY_THREAD_POOL;
}
// 使用线程池对象中的线程开线程执行代码
public void submit(Runnable runnable){
POOL_EXECUTOR.submit(runnable);
}
public <V> void submit(Callable<V> callable){
POOL_EXECUTOR.submit(callable);
}
// 延迟执行
public void delayTask(Runnable runnable,long delay,TimeUnit timeUnit){
SCHEDULED_THREAD_POOL_EXECUTOR.schedule(runnable,delay,timeUnit);
}
}
11 线程的状态
/**
* 创建出来还没启动的线程
*/
NEW,
/**
* 正在jvm中执行的线程
*/
RUNNABLE,
/**
* 受阻塞并等待的线程
*/
BLOCKED,
/**
* 无限期的等待另一个线程来执行某一个特性操作的线程
*/
WAITING,
/**
* 等待另一个线程来执行,取决于指定等待时间的操作
*/
TIMED_WAITING,
/**
* 已退出的线程
*/
TERMINATED;
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
System.out.println("Helloworld");
});
// 查看状态
Thread.State state = thread.getState();
System.out.println(state);// NEW状态
thread.start();
state = thread.getState();
System.out.println(state); //RUNNABLE
while (state != Thread.State.TERMINATED){
state = thread.getState();
System.out.println(state);
}
}
12 守护线程
在java中线程分为守护线程和被守护线程。如果一个线程不是守护线程,那么就是被守护线程。如果所有的被守护线程结束,那么守护线程也会随之结束。GC就是一个守护线程。
package cn.javasm.demo;
/***
* @className: TestDemo4
* @author: gfs
* @description:
* @date: 2024/7/2 16:54
* @version: 0.1
* @since :jdk11
*/
public class TestDemo4 {
public static void main(String[] args) {
Thread thread1 = new Thread(new Solider(),"小兵1");
Thread thread2 = new Thread(new Solider(),"小兵2");
Thread thread3 = new Thread(new Solider(),"小兵3");
// 给小兵线程设置为守护线程
thread1.setDaemon(true);
thread2.setDaemon(true);
thread3.setDaemon(true);
// 开启线程
thread1.start();
thread2.start();
thread3.start();
// main线程就是被守护线程
for (int i = 10; i >= 0; i--) {
System.out.println("BOSS还有" + i + "滴血");
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
// 小兵
class Solider implements Runnable{
@Override
public void run() {
// 假设小兵有100万血
for (int i = 1000000; i > 0; i--) {
System.out.println(Thread.currentThread().getName() + "剩余" + i + "滴血");
try {
Thread.sleep(10L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
13 线程的优先级
线程的优先级分为1-10,理论上数字越大优先级越高,抢占到资源的概率就越大。但是实际上,相邻的优先级之间的差别非常不明显。
public class TestDemo5 {
public static void main(String[] args) {
// 最大优先级
System.out.println(Thread.MAX_PRIORITY);//10
// 最小优先级 1
System.out.println(Thread.MIN_PRIORITY);
// 默认优先级 5
System.out.println(Thread.NORM_PRIORITY);
Thread thread1 = new Thread(new SDemo(),"aaa");
Thread thread2 = new Thread(new SDemo(),"bbb");
thread1.setPriority(1);
thread2.setPriority(10);
thread1.start();
thread2.start();
}
}
class SDemo implements Runnable{
@Override
public void run() {
for (int i = 10; i > 0; i--) {
System.out.println(Thread.currentThread().getName() + "正在执行");
}
}
}
标签:线程,Thread,void,static,new,public
From: https://www.cnblogs.com/460759461-zeze/p/18287669