1.程序:指指令和数据的有序集合,其自己没有任何意义,是一个静态的概念
2.进程:指执行程序的一次执行过程,是一个动态的概念。是系统资源分配的单位(注意:不少多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器。即在一个cpu的状况下,在同一时间点,cpu只能执行一个代码,由于切换的很快,因此就有同时执行的错觉)
3.线程:一般一个进程中能够包含若干个线程,一个进程中至少有一个线程。线程是cpu调度和执行的单位
4.并行:指在同一时刻,有多条指令在多个处理器上同时执行。因此不管从微观仍是从宏观来看,两者都是一块儿执行的。
5.并发:指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具备多个进程同时执行的效果,但在微观上并非同时执行的,只是把时间分红若干段,使多个进程快速交替的执行。java
核心概念:web
三种建立方式:express
//建立方式一:继承Thread类,重写run方法,调用start开启线程 public class TestThread1 extends Thread{ @Override public void run() { //run方法线程体 for (int i = 0; i < 200; i++) { System.out.println("我在打游戏-------"+i); } } public static void main(String[] args) { //main线程,主线程 //建立一个线程对象 TestThread1 testThread1 = new TestThread1(); //调用start方法,开启线程 testThread1.start(); for (int i = 0; i < 200; i++) { System.out.println("我在打飞机游戏-------"+i); } } }
最后会发现“我在打游戏”和“我在打飞机游戏”会交替执行。
注意:线程不必定当即执行,由cpu安排调度;编程
推荐使用Runnable,避免单继承的局限性,方便同一个对象被多个线程使用安全
//票数 //多个线程同时操做同一个对象 //买火车票 //发现问题,多个线程操做同一个资源的状况下,线程不安全,数据紊乱 public class TestThread04 implements Runnable{ //票数 private int ticketNums = 10; @Override public void run() { while (true){ if (ticketNums <=0 ){ break; } //模拟延时 try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"票"); } } public static void main(String[] args) { TestThread04 ticket = new TestThread04(); new Thread(ticket,"小明").start(); new Thread(ticket,"老师").start(); new Thread(ticket,"黄牛").start(); } }
老师拿到了第10票 小明拿到了第9票 黄牛拿到了第9票 老师拿到了第8票 小明拿到了第6票 黄牛拿到了第7票 黄牛拿到了第5票 老师拿到了第4票 小明拿到了第4票 黄牛拿到了第2票 小明拿到了第1票 老师拿到了第3票
能够发现,多个线程操做同一资源可能存在并发性问题服务器
龟兔赛跑案例网络
//模拟龟兔赛跑 public class Race implements Runnable{ //胜利者 private static String winner; @Override public void run() { for (int i = 0; i <= 100; i++) { //模拟兔子休息 if (Thread.currentThread().getName().equals("兔子") && i%10==0){ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } //判断比赛是否结束 boolean flag = gameOver(i); //若是比赛结束了,就中止程序 if (flag){ break; } System.out.println(Thread.currentThread().getName()+"跑了"+i+"步"); } } //判断是否完成比赛 private boolean gameOver(int steps){ if(winner!=null){//已经存在胜利者了 return true; }{ if (steps >= 100){ winner = Thread.currentThread().getName(); System.out.println("winner is "+winner); return true; } } return false; } public static void main(String[] args) { Race race = new Race(); new Thread(race,"兔子").start(); new Thread(race,"乌龟").start(); } }
下载案例多线程
//线程建立方式三:实现callable接口 public class TestCallable implements Callable<Boolean> { private String url;//网络图片地址 private String name;//保存文件名 public TestCallable(String url, String name) { this.url = url; this.name = name; } //下载图片的线程执行体 @Override public Boolean call() { WebDownLoader webDownLoader = new WebDownLoader(); webDownLoader.downLoader(url,name); System.out.println("下载了文件名为"+name); return true; } public static void main(String[] args) throws ExecutionException, InterruptedException { TestCallable t1 = new TestCallable("https://i.guancha.cn/bbs/2020/07/10/20200710142345269.jpg?imageView2/2/w/500/format/jpg","1.jpg"); TestCallable t2 = new TestCallable("https://i.guancha.cn/bbs/2020/07/10/20200710142345269.jpg?imageView2/2/w/500/format/jpg","2.jpg"); TestCallable t3 = new TestCallable("https://i.guancha.cn/bbs/2020/07/10/20200710142345269.jpg?imageView2/2/w/500/format/jpg","3.jpg"); //建立执行服务 ExecutorService ser= Executors.newFixedThreadPool(3); //提交执行 Future<Boolean> r1 = ser.submit(t1); Future<Boolean> r2 = ser.submit(t2); Future<Boolean> r3 = ser.submit(t3); //获取结果 boolean rs1 = r1.get(); boolean rs2 = r2.get(); boolean rs3 = r3.get(); System.out.println(rs1); System.out.println(rs2); System.out.println(rs3); //关闭服务 ser.shutdownNow(); } //下载器 class WebDownLoader{ //下载方法 public void downLoader(String url,String name){ try { FileUtils.copyURLToFile(new URL(url),new File(name)); } catch (IOException e) { e.printStackTrace(); System.out.println("io异常,downloader方法出现问题"); } } } }
(params)->expression[表达式] (params)->statement[语句] (params)->{statements} a->System.out.println("i like lambda-->"+a); new Thread(()->System.out.println("多线程学习。。。")).start();
为何要使用lambda表达式并发
函数式接口的定义app
任何接口,若是只包含惟一一个抽象方法,那么他就是一个函数式接口。
public interface Runnable{ public abstract void run(); }
推到lambda表达式
/* 推到lambda表达式 */ public class TestLambda1 { //3.静态内部类 static class Like2 implements ILike{ @Override public void lambda() { System.out.println("i like lambda2"); } } public static void main(String[] args) { ILike like = new Like(); like.lambda(); like = new Like2(); like.lambda(); //4.局部内部类 class Like3 implements ILike{ @Override public void lambda() { System.out.println("i like lambda3"); } } like = new Like3(); like.lambda(); //5.匿名内部类,没有类的名称,必须借助接口或者父类 like = new ILike() { @Override public void lambda() { System.out.println("i like lambda4"); } }; like.lambda(); //6.用lambda简化 ILike like1 = ()->{ System.out.println("i like lambda6"); }; like1.lambda(); } } //1.定义一个函数式接口 interface ILike{ void lambda(); } //2.实现类 class Like implements ILike{ @Override public void lambda() { System.out.println("i like lambda"); } }
案列二
public class TestLambda2 { static class Love implements ILove{ @Override public void love(int a) { System.out.println("i love u"+a); } } public static void main(String[] args) { //1.lambda表示简化 ILove love = (int a) ->{ System.out.println("i love u"+a); }; //简化1,去掉参数类型 love = (a)->{ System.out.println("i love u"+a); }; //简化2,简化括号 love = a->{ System.out.println("i love u"+a); }; //简化3,去掉花括号 love = a -> System.out.println("i love u"+a); //总结: //lambda表达式只能有一行代码的状况下才能简化成为一行,若是有多行,那么就用代码块包裹 //前提接口为函数式接口 //多个参数也能够去掉参数类型,要去掉就都去掉,必须加上括号 love.love(521); } } interface ILove{ void love(int a); }
案列
public class StaticProxy { public static void main(String[] args) { You you =new You();//你要结婚 new Thread( ()-> System.out.println("我爱你")).start(); new WeddingCompany(new You()).HappyMarry(); } } interface Marry{ //人间四大喜事 //久旱逢甘霖 //他乡遇故知 //洞房花烛夜 //金榜题名时 void HappyMarry(); } //真实角色,你去结婚 class You implements Marry{ @Override public void HappyMarry() { System.out.println("我要结婚,超开心"); } } //代理角色,帮助你结婚 class WeddingCompany implements Marry{ //代理谁==>真实目标角色 private Marry target; public WeddingCompany(Marry target) { this.target = target; } @Override public void HappyMarry() { before(); this.target.HappyMarry();//这就是真实对象 after(); } private void after() { System.out.println("结婚以后,收尾款"); } private void before() { System.out.println("结婚以前,布置现场"); } }
五大状态
setPriority(int newPriority): 更改线程的优先级
1.不推荐使用JDK提供的stop(),destroy()方法。【已废弃】
2.推荐线程本身中止下来
3.建议使用一个标志位进行终止变量当flag=false,则终止线程运行。
//测试stop //1.建议线程正常中止-->利用次数,不建议死循环 //2.建议使用标志位-->设置一个标志位 //3.不要使用stop或者destroy等过期或者JDK不建议使用的方法 public class TestStop implements Runnable{ //1.设置一个标识位 private boolean flag = true; @Override public void run() { int i = 0; while (flag){ System.out.println("run....Thread"+i++); } } //2.设置一个公开的方法中止线程,转换标志位 public void stop(){ this.flag = false; } public static void main(String[] args) { TestStop testStop = new TestStop(); new Thread(testStop).start(); for (int i = 0; i < 1000; i++) { System.out.println("main.."+i); if (i == 900){ //调用stop方法,切换标志位,让线程中止 testStop.stop(); System.out.println("线程该中止了"); } } } }
用sleep方法模拟倒计时和打印当前时间
//模拟倒计时 public class TestSleep2 { public static void main(String[] args) { //打印当前系统时间 Date startTime = new Date(System.currentTimeMillis());//获取系统当前时间 while (true){ try { Thread.sleep(1000); System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime)); startTime = new Date(System.currentTimeMillis()); } catch (InterruptedException e) { e.printStackTrace(); } } } //模拟倒计时 public static void tenDown() throws InterruptedException { int num = 10; while (true){ Thread.sleep(1000); System.out.println(num--); if (num<=0){ break; } } } }
//测试礼让线程 //礼让不必定成功,看cpu心情 public class TestYield { public static void main(String[] args) { MyYield myYield = new MyYield(); new Thread(myYield,"a").start(); new Thread(myYield,"b").start(); } } class MyYield implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"线程开始执行"); Thread.yield();//礼让 System.out.println(Thread.currentThread().getName()+"线程中止执行"); } }
//测试join方法//想象为插队 public class TestJoin implements Runnable{ @Override public void run() { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 100; i++) { System.out.println("线程vip来了"+i); } } public static void main(String[] args) throws InterruptedException { //启动咱们的线程 TestJoin testJoin = new TestJoin(); Thread thread = new Thread(testJoin); thread.start(); for (int i = 0; i < 500; i++) { if (i == 200){ thread.join();//插队 } System.out.println("main:"+i); } } }
Thread.State
线程状态。线程能够处于如下状态之一:
一个线程能够在给定的时间点处于一个状态。这些状态是不反映任何操做系统线程状态的虚拟机状态。
//观察测试线程的状态 public class TestState { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(()->{ for (int i = 0; i < 5; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("////////"); }); //观察状态 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){//只要线程不终止,就一直输出状态 Thread.sleep(100); state = thread.getState();//更新线程状态 System.out.println(state); } } }
线程的优先级用数字表示,范围从1~10
使用如下方式改变或获取优先级
优先级的设定建议再start()调度前
优先级低知识意味着得到调度的几率低,并非优先级低就不会被调用了,这都是看cpu的调度。
//测试守护线程 //上帝守护你 public class TestDaemon { public static void main(String[] args) { God god = new God(); You you = new You(); Thread thread = new Thread(god); thread.setDaemon(true);//默认是false表示是用户线程,正常的线程都是用户线程 thread.start(); new Thread(you).start();//你 用户线程启动。。。。 } } //上帝 class God implements Runnable{ @Override public void run() { while (true){ System.out.println("上帝保佑着你"); } } } //你 class You implements Runnable{ @Override public void run() { for (int i = 0; i < 36500; i++) { System.out.println("你一辈子都开心的活着"); } System.out.println("======goodbye!world============"); } }
多个线程操做同一个资源
并发:同一个对象被多个线程同时操做
线程同步:
造成条件:队列+锁
锁:
因为同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized,当一个线程得到对象的排它锁,独占资源,其余线程必须等待,使用后释放锁便可,存在如下问题:
买票不安全
//不安全的买票 //线程不安全,有负数 public class UnsafeByyTicket { public static void main(String[] args) { BuyTicket station = new BuyTicket(); new Thread(station,"我").start(); new Thread(station,"你").start(); new Thread(station,"黄牛").start(); } } class BuyTicket implements Runnable{ //票 private int ticketNums = 10; private boolean flag = true;//外部中止方式 @Override public void run() { //买票 while (flag){ try { buy(); } catch (InterruptedException e) { e.printStackTrace(); } } } private void buy() throws InterruptedException { //判断是否有票 if (ticketNums <= 0){ flag = false; return; } //模拟延时 Thread.sleep(100); //买票 System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--); } }
取钱不安全
//不安全的取钱 //两我的去银行取钱,帐户 public class UnsafeBank { public static void main(String[] args) { Account account = new Account(100,"结婚基金"); Drawing you = new Drawing(account,50,"你"); Drawing girlFriend = new Drawing(account,100,"girlFriend"); you.start(); girlFriend.start(); } } //帐户 class Account{ int money;//余额 String name;//卡名 public Account(int money, String name) { this.money = money; this.name = name; } } //银行:模拟取款 class Drawing extends Thread{ Account account;//帐户 //取了多少钱 int drawingMoney; //如今手里有多少钱 int nowMoney; public Drawing(Account account,int drawingMoney,String name){ super(name); this.account = account; this.drawingMoney = drawingMoney; } @Override public void run() { //判断有没有钱 if (account.money-drawingMoney <0){ System.out.println(Thread.currentThread().getName()+"钱不够,取不了"); return; } //sleep能够放大问题的发生性 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //卡余额=余额-你取的钱 account.money =account.money-drawingMoney; //你手里的钱 nowMoney = nowMoney+drawingMoney; System.out.println(account.name+"余额为:"+account.money); // System.out.println(this.getName()+Thread.currentThread().getName()); System.out.println(this.getName()+"手里的钱"+nowMoney); } }
不安全的集合
//线程不安全的集合 public class UnsafeList { public static void main(String[] args) throws InterruptedException { List<String> list = new ArrayList<String>(); for (int i = 0; i < 10000; i++) { new Thread(()->{ list.add(Thread.currentThread().getName()); }).start(); } Thread.sleep(5000); System.out.println(list.size()); } }
synchronized方法控制对“对象”的访问,每一个对象对应一把锁,每一个synchronized方法都必须得到调用该方法的对象的锁才能执行,不然线程会阻塞,方法一旦执行,就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能得到这个锁,继续执行
缺陷:若将一个大的方法申明为synchronized将会影响效率
同步块
Obj称之为同步监视器
同步监视器的执行过程
### 安全的案例
//不安全的取钱 //两我的去银行取钱,帐户 public class UnsafeBank { public static void main(String[] args) { Account account = new Account(1000,"结婚基金"); Drawing you = new Drawing(account,50,"你"); Drawing girlFriend = new Drawing(account,100,"girlFriend"); you.start(); girlFriend.start(); } } //帐户 class Account{ int money;//余额 String name;//卡名 public Account(int money, String name) { this.money = money; this.name = name; } } //银行:模拟取款 class Drawing extends Thread{ Account account;//帐户 //取了多少钱 int drawingMoney; //如今手里有多少钱 int nowMoney; public Drawing(Account account,int drawingMoney,String name){ super(name); this.account = account; this.drawingMoney = drawingMoney; } //取钱 //synchronized默认锁的是this @Override public void run() { synchronized (account){ //判断有没有钱 if (account.money-drawingMoney <0){ System.out.println(Thread.currentThread().getName()+"钱不够,取不了"); return; } //sleep能够放大问题的发生性 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //卡余额=余额-你取的钱 account.money =account.money-drawingMoney; //你手里的钱 nowMoney = nowMoney+drawingMoney; System.out.println(account.name+"余额为:"+account.money); // System.out.println(this.getName()+Thread.currentThread().getName()); System.out.println(this.getName()+"手里的钱"+nowMoney); } } }
//不安全的买票 //线程不安全,有负数 public class UnsafeByyTicket { public static void main(String[] args) { BuyTicket station = new BuyTicket(); new Thread(station,"我").start(); new Thread(station,"你").start(); new Thread(station,"黄牛").start(); } } class BuyTicket implements Runnable{ //票 private int ticketNums = 10; private boolean flag = true;//外部中止方式 @Override public void run() { //买票 while (flag){ try { buy(); } catch (InterruptedException e) { e.printStackTrace(); } } } //synchronized同步方法,锁的是this private synchronized void buy() throws InterruptedException { //判断是否有票 if (ticketNums <= 0){ flag = false; return; } //买票 System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--); } }
//线程不安全的集合 public class UnsafeList { public static void main(String[] args) throws InterruptedException { List<String> list = new ArrayList<String>(); for (int i = 0; i < 10000; i++) { new Thread(()->{ synchronized (list){ list.add(Thread.currentThread().getName()); } }).start(); } Thread.sleep(3000); System.out.println(list.size()); } }
//测试JUC安全类型的集合 public class TestJUC { public static void main(String[] args) { CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>(); for (int i = 0; i < 10000; i++) { new Thread(()->{ list.add(Thread.currentThread().getName()); }).start(); } try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size()); } }
死锁避免方法
产生死锁的四个必要条件:
上面列出了死锁的四个必要条件,咱们只要想办法破解其中的任意一个或多个条件就能够避免死锁发生
优先使用顺序
//测试Lock锁 public class TestLock { public static void main(String[] args) { TestLock2 testLock2 = new TestLock2(); new Thread(testLock2).start(); new Thread(testLock2).start(); new Thread(testLock2).start(); } } class TestLock2 implements Runnable{ int ticketNums = 1000; //定义lock锁 private final ReentrantLock lock = new ReentrantLock(); @Override public void run() { while (true){ try { lock.lock();//加锁 if (ticketNums > 0){ /* try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }*/ System.out.println(Thread.currentThread().getName()+" "+ticketNums--); }else { break; } }finally { lock.unlock(); } } } }
应用场景:生产者和消费者问题
这是一个线程同步问题,生产者和消费者共享同一个资源,而且生产者和消费者之间相互依赖,互为条件
在生产者消费者问题中,仅有synchronized是不够的
方法名
做用
wait()
表示线程一直等待,直到其余线程通知,与sleep不一样,会释放锁
wait(long timeout)
指定等待的毫秒数
notify()
唤醒一个处于等待状态的线程
notifyAll()
唤醒同一个对象上全部调用wait()方法的线程,优先级别高的线程优先调度
均是Object类的方法,都只能在同步方法或者同步代码块中使用,不然会抛出异常IllegalMonitorStateException
并发协做模型“生产者/消费者模式”—>管程法
//测试:生产者消费者模型--》利用缓冲区解决:管程法 //生产者,消费者,产品,缓冲区 public class TestPC { public static void main(String[] args) { SynContainer container = new SynContainer(); new Productor(container).start(); new Consumer(container).start(); } } //生产者 class Productor extends Thread{ SynContainer container; public Productor(SynContainer container){ this.container = container; } //生产 @Override public void run() { for (int i = 0; i < 100; i++) { container.push(new Chicken(i)); System.out.println("生产了"+i+"只鸡"); } } } //消费者 class Consumer extends Thread{ SynContainer container; public Consumer(SynContainer container){ this.container = container; } //消费 @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("消费了--》"+container.pop().id+"只鸡"); } } } //产品 class Chicken{ int id;//产品编号 public Chicken(int id) { this.id = id; } } //缓冲区 class SynContainer{ //须要一个容器大小 Chicken[] chickens = new Chicken[10]; //容器计数器 int count = 0; //生产者放入产品 public synchronized void push(Chicken chicken){ //若是容器满了,就须要等待消费者消费 if (count == chickens.length){ //通知消费者消费,生产者等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //若是没有满,咱们就须要丢入产品 chickens[count] = chicken; count++; //能够通知消费者消费了 this.notifyAll(); } //消费者消费产品 public synchronized Chicken pop(){ //判断可否消费 if (count == 0){ //等待生产者生产,消费者等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //若是能够消费 count--; Chicken chicken = chickens[count]; //吃完了,通知生产者生产 this.notifyAll(); return chicken; } }
package com.kuang.gaoji; //测试生产者消费者问题2:信号灯法,标志位解决 public class TestPc2 { public static void main(String[] args) { TV tv = new TV(); new Player(tv).start(); new Watcher(tv).start(); } } //生产者--》演员 class Player extends Thread{ TV tv; public Player(TV tv){ this.tv = tv; } @Override public void run() { for (int i = 0; i < 20; i++) { if (i%2 == 0){ this.tv.play("快乐大本营播放着"); }else { this.tv.play("抖音:记录美好生活"); } } } } //消费者--》观众 class Watcher extends Thread{ TV tv; public Watcher(TV tv){ this.tv = tv; } @Override public void run() { for (int i = 0; i < 20; i++) { tv.watch(); } } } //产品--》节目 class TV{ //演员表演时,观众等待 //观众观看,演员等待 String voice;//表演的节目 boolean flag = true; //表演 public synchronized void play(String voice){ if (flag!= true){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("演员表演了"+voice); //通知观众观看 this.notifyAll();//通知唤醒 this.voice = voice; this.flag = !this.flag; } //观看 public synchronized void watch(){ if (flag == true){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("观众观看了"+voice); //通知演员表演 this.notifyAll(); this.flag = !this.flag; } }
好处:
便于线程管理(…)
ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
//测试线程池 public class TestPool { public static void main(String[] args) { //1.建立线程池 //newFixedThreadPool 参数为:线程池大小 ExecutorService service = Executors.newFixedThreadPool(10); //执行 service.execute(new MyThread()); service.execute(new MyThread()); service.execute(new MyThread()); service.execute(new MyThread()); //2.关闭连接 service.shutdown(); } } class MyThread implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()); } }
//回顾总结线程的建立 public class ThreadNew { public static void main(String[] args) { new MyThread1().start(); new Thread(new MyThread2()).start(); FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3()); new Thread(futureTask).start(); try { Integer integer = futureTask.get(); System.out.println(integer); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } //1.继承Thread类 class MyThread1 extends Thread{ @Override public void run(){ System.out.println("MyThread1"); } } //2.实现Runnable接口 class MyThread2 implements Runnable{ @Override public void run() { System.out.println("MyThread2"); } } //3.实现Callable接口 class MyThread3 implements Callable<Integer>{ @Override public Integer call() throws Exception { System.out.println("MyThread3"); return 100; } }
感谢你看到这里,看完有什么的不懂的能够在评论区问我,以为文章对你有帮助的话记得给我点个赞,天天都会分享java相关技术文章或行业资讯,欢迎你们关注和转发文章!