Java_多线程

        有了多线程,我们就可以让程序同时做多件事情

作用:

        提高效率

应用场景:

        只要想让多个事情同时运行就需要用到多线程

        比如:软件中的耗时操作、所有的聊天软件、所有的服务器...

并发和并行

       并发:在同一时刻,有多个指令在单个CPU上交替执行

        并行:在同一时刻,有多个指令在多个CPU上同时执行

多线程的实现方式:

①继承Thread类的方式进行实现

实现步骤:

        1.自己定义一个类继承Thread

        2.重写run方法

        3.创建子类对象,并启动线程

代码演示:
        MyThread类(继承Thread):
public class MyThread extends Thread {@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(getName() + "aaa");}}
}
        测试类ThreadDemo1:
public class ThreadDemo1 {public static void main(String[] args) {/*1.创建类继承Thread2.重写run方法3.创建子类对象,启动线程*///创建子类对象MyThread t1 = new MyThread();MyThread t2 = new MyThread();//给线程起名字便于观察结果t1.setName("线程1");t2.setName("线程2");//启动线程t1.start();t2.start();}
}
        运行结果:

(只截取了一部分)两个进程并发执行

        

②实现Runnable接口的方式进行实现

 实现步骤:

        1.定义一个类implements Runnable接口

        2.重写run方法

        3.创建这个类的对象

        4.创建线程对象,启动线程

代码演示:
        MyRun类(实现Runnable):
public class MyRun implements Runnable{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + "aaa");}}
}
        测试类ThreadDemo2:
public class ThreadDemo2 {public static void main(String[] args) {/*1.定义一个类implements Runnable接口2.重写run方法3.创建这个类的对象4.创建线程对象,启动线程*///创建这个类的对象MyRun mr = new MyRun();//创建线程对象Thread t1 = new Thread(mr);Thread t2 = new Thread(mr);//起名字t1.setName("线程1");t2.setName("线程2");//启动线程t1.start();t2.start();}
}
        运行结果:

(只截取了一部分)两个进程并发执行

        

★③利用Callable接口和Future接口方式实现

代码演示:

         MyCallable类(实现Callable):
public class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 0; i < 100; i++) {sum = sum + i;}return sum;}
}
        测试类ThreadDemo3:
public class ThreadDemo3 {public static void main(String[] args) throws ExecutionException, InterruptedException {/*1.定义一个类MyCallable实现Callable接口2.重写call方法(有返回值,表示多线程运行的结果)3.创建MyCallable的对象(表示多线程要执行的任务)4.创建FutureTask的对象(作用是管理多线程运行的结果)5.创建Thread类的对象,启动线程*///创建MyCallable的对象MyCallable mc = new MyCallable();//创建FutureTask的对象FutureTask<Integer> ft = new FutureTask<>(mc);//创建Thread类的对象Thread t1 = new Thread(ft);//启动线程t1.start();//获取结果Integer result = ft.get();System.out.println(result);}
}
        运行结果:

        

多线程三种实现方式对比

常见成员方法:

        其中前四种方法比较简单,在此简单介绍几点

        1.线程的默认名字是Thread-序号,序号从0开始,随着进程创建按顺序逐个+1

        2.第三个第四个方法都是哪个线程实现这两个成员方法所在的方法,则是对这个线程操作

        3.让线程休眠后续代码也会运行

优先级:

        线程的优先级从1~10分为10挡,1为优先级最低,10为最高。

        优先级越高,在线程并发中抢到CPU的概率就更高(但不是绝对的)。

        线程的默认优先级都为5。

代码演示:
MyThread类:
public class MyThread extends Thread {@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(getName() + ":" + i);}}
}
测试类ThreadDemo4:
public class ThreadDemo4 {public static void main(String[] args) {//创建进程MyThread t1 = new MyThread();MyThread t2 = new MyThread();//设置优先级t1.setPriority(10);t2.setPriority(1);//设置名字t1.setName("线程1");t2.setName("线程2");t1.start();t2.start();}
}
运行结果:

守护线程:

        特点:

                当其他非守护线程执行完毕之后,守护线程也会陆续结束,不管是否执行完代码。

代码演示:
MyThread1:
public class MyThread1 extends Thread {@Overridepublic void run() {for (int i = 0; i <= 100; i++) {System.out.println(getName() + ":" + i);}}
}
MyThread2:
public class MyThread2 extends Thread {@Overridepublic void run() {for (int i = 0; i <= 10; i++) {System.out.println(getName() + ":" + i);}}
}
测试类ThreadDemo5:
public class ThreadDemo5 {public static void main(String[] args) {//创建进程MyThread1 t1 = new MyThread1();MyThread2 t2 = new MyThread2();//设置名字t1.setName("沸羊羊");t2.setName("美羊羊");t1.setDaemon(true);t1.start();t2.start();}
}
运行结果:

出让线程:

        执行Thread.yield命令后就是把CPU的执行权交出,然后各个线程重新抢夺。

插队线程:

        执行方法 ‘线程对象.join()’ 后 表示把这个线程,插入到当前线程之前,先执行完这个线程,再执行当前方法的线程。

线程的生命周期

线程的生命周期是:新建状态,就绪状态,运行状态,阻塞状态,死亡状态

虚拟机中线程的六种状态

新建状态、就绪状态、阻塞状态、等待状态、计时等待、结束状态 (没有运行状态)

多线程代码编写核心逻辑

        1.循环

        2.同步代码块

        3.判断共享数据是否到了末尾(到了末尾)

        4.判断共享数据是否到了末尾(没到末尾,执行核心逻辑)

同步代码块:

格式:

        synchronized (锁对象) {

                同步代码块

        }

特点:

        当一个线程抢到CPU的执行权进入到同步代码块中执行代码了,那么其他的线程是不能再进来同步代码块的,只有当成功进入的线程执行完毕出去之后,它的锁才打开,其他的线程才能进来,而且也只能进去一个线程。

        并且,如果锁对象不唯一,那么相当于有好几把锁,就可能出现不同的线程看的是不同的锁,还是会同时进去执行代码。一般我们用当前类的字节码文件作为锁对象(类名.class).

        先结合一个小练习进行代码演示

练习:

        某电影院目前正在上映国产大片,共有1000张票,而它有3个卖票窗口,请设计一个程序模拟该电影院买票。

代码演示:
MyThread3类:
public class MyThread3 extends Thread {//表示这个类所有的对象,都共享ticket数据static int ticket = 0;//锁对象,必须是唯一的@Overridepublic void run() {while (true) {//同步代码块synchronized (MyThread3.class) {if(ticket < 1000) {try {Thread.sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}ticket++;System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票");} else {break;}}}}
}
测试类ThreadDemo6:
public class ThreadDemo6 {public static void main(String[] args) {//某电影院目前正在上映国产大片,共有1000张票,而它有3个卖票窗口,请设计一个程序模拟该电影院买票。//创建三个线程对象MyThread3 t1 = new MyThread3();MyThread3 t2 = new MyThread3();MyThread3 t3 = new MyThread3();//设置名字t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");//启动线程t1.start();t2.start();t3.start();}
}
运行结果:

同步方法:

        同步方法就是把synchronized关键字加到方法上

格式:

        修饰符 synchronized 返回值类型 方法名(方法参数) {...}

特点:

        1.同步方法是锁住方法里面所有的代码

        2.锁对象不能自己指定(非静态方法中是this,静态方法中是当前类的字节码文件对象)

        继续结合上述小练习进行代码演示

练习:

        某电影院目前正在上映国产大片,共有1000张票,而它有3个卖票窗口,请设计一个程序模拟该电影院买票。

代码演示:
MyRunnable类:
public class MyRunnable implements Runnable {int ticket = 0;@Overridepublic void run() {while(true) {if (method()) break;}}//同步方法private synchronized boolean method() {if(ticket == 1000) {return true;} else {try {Thread.sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}ticket++;System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票");}return false;}
}
测试类ThreadDemo7:
public class ThreadDemo7 {public static void main(String[] args) {//某电影院目前正在上映国产大片,共有1000张票,而它有3个卖票窗口,请设计一个程序模拟该电影院买票。MyRunnable mr = new MyRunnable();Thread t1 = new Thread(mr);Thread t2 = new Thread(mr);Thread t3 = new Thread(mr);t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();}
}
运行结果:

lock锁:

        继续结合上述小练习进行代码演示

练习:

        某电影院目前正在上映国产大片,共有1000张票,而它有3个卖票窗口,请设计一个程序模拟该电影院买票。

代码演示:
MyThread类:
public class MyThread extends Thread {static int ticket = 0;//创建锁对象static Lock lock = new ReentrantLock();@Overridepublic void run() {while(true) {lock.lock();try {if(ticket == 1000) {break;} else {Thread.sleep(10);ticket++;System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票");}} catch (InterruptedException e) {throw new RuntimeException(e);} finally {lock.unlock();}}}
}
测试类ThreadDemo8:
public class ThreadDemo8 {public static void main(String[] args) {//某电影院目前正在上映国产大片,共有1000张票,而它有3个卖票窗口,请设计一个程序模拟该电影院买票。//创建线程MyThread t1 = new MyThread();MyThread t2 = new MyThread();MyThread t3 = new MyThread();//设置名字t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");//启动线程t1.start();t2.start();t3.start();}
}
运行结果:

死锁:

概念:

        死锁是多线程中的一种错误

        举个例子:

        两个人在一起吃饭,桌上只有一双筷子,有三个条件

                ①每次需要拿起筷子才能吃饭

                ②一次只能拿一只筷子

                ③拿到一双筷子后可以吃一口

        在一个人拿到一只筷子,另一个人也拿到一只筷子时,这时候就发生了死锁,程序无法结束

注意:

        关于死锁需要注意,在设计程序时尽量避免发生锁的嵌套

生产者和消费者(等待唤醒机制)

1.基本写法:

        假如现在有一个厨师和一个食客,食客吃10份食物就饱了,当桌子上有食物时,食客就会吃一份,吃完通知厨师再做一份,如果没有食物,就等待。厨师在桌子上没有食物时,就再做一份,如果有食物则等待。

代码演示:
桌子Desk类:
public class Desk {//定义变量表示桌子上是否有食物 0:没有 1:有public static int foodFlag = 0;//定义变量存储食客还能吃几碗 食客最多能吃10碗 初始值为10public static int count = 10;//锁对象public static Object lock = new Object();
}
食客Foodie类:
public class Foodie extends Thread {@Overridepublic void run() {while (true) {synchronized (Desk.lock) {//判断还能吃吗if(Desk.count == 0) {//不能吃了break;} else {//还能吃//判断桌子上有没有食物if(Desk.foodFlag == 0) {//如果没有食物//等待(调用锁对象的wait方法让食客线程阻塞)try {Desk.lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}} else {//如果有食物Desk.count--;if(Desk.count == 0) {System.out.println("食客吃完了一份食物,吃饱了");} else {System.out.println("食客吃完了一份食物,还能吃" + Desk.count + "份");}//将桌子上置为没有食物Desk.foodFlag = 0;//通知厨师Desk.lock.notifyAll();//唤醒这个锁对象内的所有线程}}}}}
}
厨师Cooker类:
public class Cooker extends Thread {@Overridepublic void run() {while (true) {synchronized (Desk.lock) {//判断食客是否吃饱了if(Desk.count == 0) {//吃饱了break;} else {//没吃饱//判断桌子上是否有食物if(Desk.foodFlag == 1) {//有食物try {Desk.lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}} else {//没有食物System.out.println("厨师制作好了食物放到了桌子上");//将桌子置为有食物Desk.foodFlag = 1;//唤醒食客Desk.lock.notifyAll();//唤醒这个锁对象内的所有线程}}}}}
}
测试类Test:
public class Test {public static void main(String[] args) {//创建线程对象Foodie foodieThread = new Foodie();Cooker cookerThread = new Cooker();//启动线程foodieThread.start();cookerThread.start();}
}
运行结果:

2.利用阻塞队列:

        假如现在有一个厨师和一个食客,食客吃10份食物就饱了,厨师可以不断的做好食物并放到窗口上,窗口上最多可以放3碗。当窗口上有食物时,食客就会吃,如果没有食物,就等待。

代码演示:
食客Foodie类:
public class Foodie extends Thread {ArrayBlockingQueue<String> queue;public Foodie(ArrayBlockingQueue<String> queue) {this.queue = queue;}@Overridepublic void run() {while (true) {try {String food = queue.take();System.out.println("食客吃了一份" + food);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
厨师Cooker类:
public class Cooker extends Thread {ArrayBlockingQueue<String> queue;public Cooker(ArrayBlockingQueue<String> queue) {this.queue = queue;}@Overridepublic void run() {while (true) {try {queue.put("面条");System.out.println("厨师放了一碗面条");} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
测试类Test:
public class Test {public static void main(String[] args) {/*假如现在有一个厨师和一个食客,厨师可以不断的做好食物并放到窗口上,窗口上最多可以放3碗。当窗口上有食物时,食客就会吃,如果没有食物,就等待。*/ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3);Foodie foodieThread = new Foodie(queue);Cooker cookerThread = new Cooker(queue);foodieThread.start();cookerThread.start();}
}
运行结果:

(因为输出语句在锁的外面,所以输出结果不一定和真是数据传输一致)

练习题

练习一:

        一共有1000张电影票,可以在两个窗口领取,假设每次领取的时间为3000毫秒,

        要求:用多线程模拟卖票过程并打印剩余电影票的数量

代码演示:
MyThread类:
public class MyThread extends Thread {//定义还剩多少票static int ticket = 1000;@Overridepublic void run() {while(true) {synchronized (MyThread.class) {//判断是否还有票if(ticket == 0) {//没票了break;} else {//有票//每次领取时间3000毫秒try {sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}ticket--;if(ticket != 0) {System.out.println(getName() + "卖了一张票,还剩" + ticket + "张票");} else {System.out.println(getName() + "卖了一张票,票卖完了");}}}}}
}
测试类Test:
public class Test {public static void main(String[] args) {/*一共有1000张电影票,可以在两个窗口领取,假设每次领取的时间为3000毫秒,要求:用多线程模拟卖票过程并打印剩余电影票的数量*///创建线程对象MyThread t1 = new MyThread();MyThread t2 = new MyThread();//设置名字t1.setName("窗口1");t2.setName("窗口2");//启动线程t1.start();t2.start();}
}
运行结果:

 练习二:

        有100份礼品,两人同时发送,当剩下的礼品小于10份的时候则不再发出

        利用多线程模拟该过程并将线程的名字和礼物的剩余数量打印出来

代码演示:
MyThread类:
public class MyThread extends Thread {//剩余礼物数量static int gift = 100;@Overridepublic void run() {while (true) {synchronized (MyThread.class) {//判断礼物数量if(gift == 9) {//礼物数量小于10break;} else {//礼物数量不小于10gift--;System.out.println(getName() + "分发了一份礼物,还剩" + gift + "份");}}}}
}
测试类Test:
public class Test {public static void main(String[] args) {/*有100份礼品,两人同时发送,当剩下的礼品小于10份的时候则不再发出利用多线程模拟该过程并将线程的名字和礼物的剩余数量打印出来*///创建线程对象MyThread t1 = new MyThread();MyThread t2 = new MyThread();//设置名字t1.setName("分发员1");t2.setName("分发员2");//启动线程t1.start();t2.start();}
}
运行结果:

 练习三:

        同时开启了两个线程,共同获取1~100之间的所有数字

        要求:输出所有的奇数

代码演示:
MyThread类:
public class MyThread extends Thread {static int num = 1;@Overridepublic void run() {while (true) {synchronized (MyThread.class) {if(num > 100) {break;} else {if(num % 2 == 1) {System.out.println(getName() + ":" + num);}num++;}}}}
}
测试类Test:
public class Test {public static void main(String[] args) {/*同时开启了两个线程,共同获取1~100之间的所有数字要求:输出所有的奇数*/MyThread t1 = new MyThread();MyThread t2 = new MyThread();t1.start();t2.start();}
}
运行结果:

 练习四:

        抢红包也用到了多线程

        假设:100块,分成了3个包,现在有五个人去抢

        其中,红包是共享数据 5个人是5条线程

        打印结果如下:

                XXX抢到了XXX元

                XXX抢到了XXX元

                XXX抢到了XXX元

                XXX没抢到

                XXX没抢到

代码演示:
MyThread类:
public class MyThread extends Thread {//红包个数static int count = 3;//红包中剩余金额static BigDecimal money = BigDecimal.valueOf(100);//红包金额最小值static final BigDecimal MIN = BigDecimal.valueOf(0.01);@Overridepublic void run() {//一人抢一次所以不用循环synchronized (MyThread.class) {//对红包数量判断if(count == 0) {//抢完了System.out.println(getName() + "没抢到红包");} else {//没抢完BigDecimal prize;if(count == 1) {//只剩一个红包prize = money;} else {//还剩多个红包Random r = new Random();double bounds = money.subtract(MIN.multiply(BigDecimal.valueOf(count - 1))).doubleValue();prize = BigDecimal.valueOf(r.nextDouble(bounds));if (prize.compareTo(MIN) == -1) {//prize小于MINprize = MIN;}}//设置保留两位小数,四舍五入prize = prize.setScale(2, RoundingMode.HALF_UP);System.out.println(getName() + "抢到了" + prize + "元");//从红包金额中减去抢到的金额money = money.subtract(prize);//红包个数减一count--;}}}
}
测试类Test:
public class Test {public static void main(String[] args) {/*抢红包也用到了多线程假设:100块,分成了3个包,现在有五个人去抢其中,红包是共享数据5个人是5条线程打印结果如下:XXX抢到了XXX元XXX抢到了XXX元XXX抢到了XXX元XXX没抢到XXX没抢到*/MyThread t1 = new MyThread();MyThread t2 = new MyThread();MyThread t3 = new MyThread();MyThread t4 = new MyThread();MyThread t5 = new MyThread();t1.setName("小A");t2.setName("小B");t3.setName("小C");t4.setName("小D");t5.setName("小E");t1.start();t2.start();t3.start();t4.start();t5.start();}
}
运行结果:

​​​​​​​

 练习五:

        有一个抽奖池,该抽奖池中存放了奖励的金额,

        该抽奖池中的奖项为: {10,5,20,50,100,200,500,800,2,80,300,700}

        创建两个抽奖箱(线程) 随机从抽奖池中获取奖项元素并打印在控制台上

        格式如下:

                抽奖箱1产生了一个10元大奖

                抽奖箱2产生了一个100元大奖

                抽奖箱2产生了一个800元大奖

                抽奖箱1产生了一个200元大奖

                ...

代码演示:
MyThread类:
public class MyThread extends Thread {ArrayList<Integer> list;public MyThread(ArrayList<Integer> list) {this.list = list;}@Overridepublic void run() {while((true)) {synchronized (MyThread.class) {if(list.isEmpty()) {//集合为空break;} else {//集合不为空Collections.shuffle(list);int prize = list.remove(0);System.out.println(getName() + "产生了一个" + prize + "元大奖");}}//锁外睡10毫秒,避免结果只出现一个线程try {sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
测试类Test:
public class Test {public static void main(String[] args) {/*有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为:{10,5,20,50,100,200,500,800,2,80,300,700}创建两个抽奖箱(线程)随机从抽奖池中获取奖项元素并打印在控制台上格式如下:抽奖箱1产生了一个10元大奖抽奖箱2产生了一个100元大奖抽奖箱2产生了一个800元大奖抽奖箱1产生了一个200元大奖...*///创建抽奖池ArrayList<Integer> list = new ArrayList<>();Collections.addAll(list,10,5,20,50,100,200,500,800,2,80,300,700);MyThread t1 = new MyThread(list);MyThread t2 = new MyThread(list);t1.setName("抽奖箱1");t2.setName("抽奖箱2");t1.start();t2.start();}
}
运行结果:

​​​​​​​​​​​​​​​​​​​​​​​​​​​​

 练习五Pro1:

        在上一题(练习五)的基础上继续完成如下需求:

         每次抽的过程中,不打印,抽完时一次性打印(随机)

        格式如下:

                在此次抽奖过程中,抽奖箱1总共产生了6个奖项

                分别为:10,20,100,500,2,300最高奖项为300元,总计额为932元

                在此次抽奖过程中,抽奖箱2总共产生了6个奖项

                分别为:5,50,200,800,80,700最高奖项为800元,总计额为1835元

代码演示:
MyThread类:
public class MyThread extends Thread {ArrayList<Integer> list;int max = 0;int sum = 0;public MyThread(ArrayList<Integer> list) {this.list = list;}public String boxToString(ArrayList<Integer> box) {StringBuffer sb = new StringBuffer();for (int i = 0; i < box.size(); i++) {if (i != box.size() - 1) {sb.append(box.get(i) + ",");} else {sb.append(box.get(i));}}return sb.toString();}@Overridepublic void run() {ArrayList<Integer> box = new ArrayList<>();while ((true)) {synchronized (com.han.thread.test5.MyThread.class) {if (list.isEmpty()) {//集合为空System.out.println("在此次抽奖过程中," + getName() + "总共产生了" + box.size() + "个奖项\n" +"分别为:" + boxToString(box) + "最高奖项为" + max + "元,总计额为" + sum + "元");break;} else {//集合不为空Collections.shuffle(list);int prize = list.remove(0);box.add(prize);sum = sum + prize;if (prize > max) {max = prize;}//System.out.println(getName() + "产生了一个" + prize + "元大奖");}}//锁外睡10毫秒,避免结果只出现一个线程try {sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
测试类Test:
public class Test {public static void main(String[] args) {/*在上一题(练习五)的基础上继续完成如下需求:每次抽的过程中,不打印,抽完时一次性打印(随机)格式如下:在此次抽奖过程中,抽奖箱1总共产生了6个奖项分别为:10,20,100,500,2,300最高奖项为300元,总计额为932元在此次抽奖过程中,抽奖箱2总共产生了6个奖项分别为:5,50,200,800,80,700最高奖项为800元,总计额为1835元*///创建抽奖池ArrayList<Integer> list = new ArrayList<>();Collections.addAll(list,10,5,20,50,100,200,500,800,2,80,300,700);MyThread t1 = new MyThread(list);MyThread t2 = new MyThread(list);t1.setName("抽奖箱1");t2.setName("抽奖箱2");t1.start();t2.start();}
}
运行结果:

​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

 练习五Pro2:

        在上一题基础上继续完成如下需求:

         每次抽的过程中,不打印,抽完时一次性打印(随机)

        格式如下:

                在此次抽奖过程中,抽奖箱1总共产生了6个奖项

                分别为:10,20,100,500,2,300最高奖项为300元,总计额为932元

                在此次抽奖过程中,抽奖箱2总共产生了6个奖项

                分别为:5,50,200,800,80,700最高奖项为800元,总计额为1835元

                在此次抽奖过程中,抽奖箱2中产生了最大奖项,该奖项金额为800元

代码演示:
MyCallable类:
public class MyCallable implements Callable<Integer> {ArrayList<Integer> list;public MyCallable(ArrayList<Integer> list) {this.list = list;}public String boxToString(ArrayList<Integer> box) {StringBuffer sb = new StringBuffer();for (int i = 0; i < box.size(); i++) {if (i != box.size() - 1) {sb.append(box.get(i) + ",");} else {sb.append(box.get(i));}}return sb.toString();}@Overridepublic Integer call() throws Exception {int max = 0;int sum = 0;ArrayList<Integer> box = new ArrayList<>();while ((true)) {synchronized (com.han.thread.test5.MyThread.class) {if (list.isEmpty()) {//集合为空System.out.println("在此次抽奖过程中," + Thread.currentThread().getName() + "总共产生了" + box.size() + "个奖项\n" +"分别为:" + boxToString(box) + "最高奖项为" + max + "元,总计额为" + sum + "元");break;} else {//集合不为空Collections.shuffle(list);int prize = list.remove(0);box.add(prize);sum = sum + prize;if (prize > max) {max = prize;}//System.out.println(getName() + "产生了一个" + prize + "元大奖");}}//锁外睡10毫秒,避免结果只出现一个线程Thread.sleep(10);}if(box.isEmpty()) {return null;} else {return Collections.max(box);}}}
测试类Test:
public class Test {public static void main(String[] args) throws ExecutionException, InterruptedException {/*在上一题基础上继续完成如下需求:每次抽的过程中,不打印,抽完时一次性打印(随机)格式如下:在此次抽奖过程中,抽奖箱1总共产生了6个奖项分别为:10,20,100,500,2,300最高奖项为300元,总计额为932元在此次抽奖过程中,抽奖箱2总共产生了6个奖项分别为:5,50,200,800,80,700最高奖项为800元,总计额为1835元在此次抽奖过程中,抽奖箱2中产生了最大奖项,该奖项金额为800元*///创建抽奖池ArrayList<Integer> list = new ArrayList<>();Collections.addAll(list, 10, 5, 20, 50, 100, 200, 500, 800, 2, 80, 300, 700);//创建多线程要运行的参数对象MyCallable mc = new MyCallable(list);//创建多线程运行结果的管理者对象FutureTask<Integer> ft1 = new FutureTask<>(mc);FutureTask<Integer> ft2 = new FutureTask<>(mc);//创建线程对象Thread t1 = new Thread(ft1);Thread t2 = new Thread(ft2);//设置名字t1.setName("抽奖箱1");t2.setName("抽奖箱2");//启动t1.start();t2.start();//输出结果int max1 = ft1.get();int max2 = ft2.get();if (max1 > max2) {System.out.println("在此次抽奖过程中,抽奖箱1中产生了最大奖项,该奖项金额为" + max1 + "元");} else {System.out.println("在此次抽奖过程中,抽奖箱2中产生了最大奖项,该奖项金额为" + max2 + "元");}}
}
运行结果:

​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

线程池

核心原理:

        ①创建一个池子,池子中是空的

        ②提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子,下回再次提交任务时,不需要创建新的线程,直接复用已有的线程即可

        ③但是如果提交任务时,池子中没有空闲的线程,也无法创建新的线程,任务就会排队等待

创建方法:

代码演示:

MyRunnable类:
public class MyRunnable implements Runnable {@Overridepublic void run() {for (int i = 1; i <= 1000; i++) {System.out.println(Thread.currentThread().getName() + "-" + i);}}
}
测试类ThreadPoolDemo1:
public class ThreadPoolDemo1 {public static void main(String[] args) throws InterruptedException {//创建一个没有上限的线程池(上限二十一亿多,但是创建不了这么多机器就承受不住)ExecutorService pool1 = Executors.newCachedThreadPool();System.out.println("无上限线程池");pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());//销毁线程池pool1.shutdown();//创建一个有上线的线程池ExecutorService pool2 = Executors.newFixedThreadPool(3);System.out.println("有上限线程池");pool2.submit(new MyRunnable());pool2.submit(new MyRunnable());pool2.submit(new MyRunnable());pool2.submit(new MyRunnable());pool2.submit(new MyRunnable());//销毁线程池pool2.shutdown();}
}

自定义线程池

核心原理:

        ①创建一个池子,池子中是空的

        ②有任务提交时,线程池会创建线程去执行任务,执行完毕归还线程

不断的提交任务,会有以下三个临界点

        ①当核心线程满时,再提交任务就会排队

        ②当核心线程满,队伍满时,会创建临时线程

        ③当核心线程满,队伍满,临时线程满时,会触发任务拒绝策略

任务拒绝策略:

创建代码演示:

public class ThreadPoolDemo2 {public static void main(String[] args) {/*ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(核心线程数量,最大线程数量,空闲线程最大存活时间,时间单位,任务队列,创建线程工厂,任务的拒绝策略)参数一:核心线程数量              不能小于0参数二:最大线程数量              不能小于0,最大数量>=核心线程数量参数三:空闲线程最大存活时间       不能小于0参数四:时间单位                  用TimeUnit指定参数五:任务队列                  不能为null参数六:创建线程工厂              不能为null参数七:任务的拒绝策略             不能为null*/ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,6,60,TimeUnit.SECONDS,new ArrayBlockingQueue<>(3),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());}
}

线程池多大合适

前置概念:

最大并行数:

        如果电脑是四核八线程,那么最大并行数就是8

        我的电脑是八核十六线程,所以最大并行数是16

1.CPU密集型运算:

        计算比较多,读取本地文件或者数据库的操作比较少

线程池大小:

        这种情况线程池大小最好为最大并行数+1=17,多出来那一个用来候补,,在遇到问题时顶上去

2.I/O密集型运算:

        读取本地文件或者数据库的操作比较多

线程池大小:

        有下面公式求出

​​​​​​​

        这里期望CPU利用率我们设为100%,假如要从本地文件中读取两个数据并进行相加,获取数据由硬盘完成,不计算在CPU计算时间中,假设读取数据用1秒,CPU计算用1秒,那么总时间就为2秒,线程池大小就等于16(最大并行数)* 100% * 2/1 = 32

        CPU计算时间可以通过thread dump工具类获取,然后计算

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/14853.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI是否可统计人类理性和感性的非线性?

一、背景 从控制理论的角度来看&#xff0c;“人类理性和感性的非线性”可以类比为动态系统中非线性元件的行为特性。在控制理论中&#xff0c;非线性意味着系统的输出不再严格与其输入成比例&#xff0c;也就是说&#xff0c;同样的输入条件下可能会导致不同的结果&#xff0…

当面试官问出“Unsafe”类时,我就知道这场面试废了,祖坟都能给你问出来!

一、写在开头 依稀记得多年以前的一场面试中&#xff0c;面试官从Java并发编程问到了锁&#xff0c;从锁问到了原子性&#xff0c;从原子性问到了Atomic类库&#xff08;对着JUC包进行了刨根问底&#xff09;&#xff0c;从Atomic问到了CAS算法&#xff0c;紧接着又有追问到了…

前后端开发入门全攻略:零基础学起

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、前后端开发概览 二、后端开发基础&#xff1a;Flask框架入门 代码案例&#xff1a;Hel…

vue3之使用图片实现类似于 el-radio 的单选框功能,并且可实现选中和取消选中

背景 我们在工作中常用的一般都是使用类似于 element-plus 中的 el-radio 或者是 el-checkbox 来实现单选或者多选 若有一天我们遇到了一个新的业务需求,需要使用 图片 来实现类似于 el-radio 的功能,并且要求实现第一次点击时处于选中状态,当我们再次点击时处于非选中状态…

谈恋爱没经验?那就来刷谈恋爱经验宝宝吧

❤️作者主页&#xff1a;小虚竹 ❤️作者简介&#xff1a;大家好,我是小虚竹。2022年度博客之星评选TOP 10&#x1f3c6;&#xff0c;Java领域优质创作者&#x1f3c6;&#xff0c;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;掘金年度人气作…

自动驾驶---Tesla的自动驾驶技术进化史(PerceptionPlanning)

1 前言 笔者在专栏《自动驾驶Planning模块》中已经详细讲解了传统自动驾驶Planning模块的内容&#xff1a;包括行车的Behavior Planning和Motion Planning&#xff0c;以及低速记忆泊车的Planning&#xff08;最开始有15篇&#xff0c;目前逐渐更新到17篇&#xff09;。读者对整…

【Spring】SSM介绍_SSM整合

1、SSM介绍 1.1简介 SSM&#xff08;Spring SpringMVC MyBatis&#xff09;整合是一种流行的Java Web应用程序框架组合&#xff0c;它将Spring框架的核心特性、SpringMVC作为Web层框架和MyBatis作为数据访问层框架结合在一起。这种整合方式提供了从数据访问到业务逻辑处理再…

5.18 TCP机械臂模拟

#include <netinet/tcp.h>//包含TCP选项的头文件 #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <linux/input.h>//读取输入事件 #include <sys/types.h> #include <sys/stat.h&…

一文详解逻辑越权漏洞

1. 逻辑越权 1.1. 漏洞原理 逻辑越权漏洞就是当用户跳过自己的权限限制&#xff0c;去操作同等级用户或者上级用户。正常的情况下&#xff0c;当一个用户去访问某个资源的时候&#xff0c;首先需要去登录验证自己的权限&#xff0c;其次是对数据的查询&#xff0c;最后返回数…

linux命令中arpd的使用

arpd 收集免费ARP信息 补充说明 arpd命令 是用来收集免费arp信息的一个守护进程&#xff0c;它将收集到的信息保存在磁盘上或者在需要时&#xff0c;提供给内核用户用于避免多余广播。 语法 arpd(选项)(参数)选项 -l&#xff1a;将arp数据库输出到标准输出设备显示并退出…

【云原生】Kubernetes----POD基本管理

目录 引言 一、Pod基础概念 &#xff08;一&#xff09;Pod简介 &#xff08;二&#xff09;Pod的分类 1.自主式Pod 2.控制器管理的Pod &#xff08;三&#xff09;Pod使用方式 1.单容器pod 2.多容器Pod 3. 注意事项 二、Pod容器的分类 &#xff08;一&#xff09;…

【Unity】免费的高亮插件——QuickOutline

除了常见的HighLightSystem来实现的高亮功能&#xff0c;其实还有很多的方法实现物体的高亮。 在 Unity资源商店 搜索OutLine&#xff0c;就会有很多免费好用的高亮插件。 下面介绍一下 QuickOutline这个插件&#xff0c;在 Unity资源商店 搜索到后&#xff0c;点击进去就可以…

推荐几款新手学习编程的网站

免费在线开发平台 介绍一款编程平台&#xff0c;专为学生和开发者量身打造&#xff01;平台拥有近4000道编程题目&#xff0c;支持多种编程语言&#xff08;包括C、C、JavaScript、TypeScript、Go、Rust、PHP、Java、Ruby、Python3和C#&#xff09;&#xff0c;为您提供全面的学…

Tomcat端口配置

Tomcat是开源免费的服务器&#xff0c;其默认的端口为8080&#xff0c;本文讲述一下如何配置端口。 最后在浏览器中输入localhost:8888即可打开Tomcat界面

python判断字符串是否为回文串的详细解析与实现

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言&#xff1a;回文串的定义与背景 二、判断回文串的基本思路 示例解析 三、代码实…

三维场景感知之三维目标检测方向入门

三维目标检测入门 1 文档需知2 基础知识深度学习基础必上手项目科研研究必知道的论文门户深度学习必看论文 3 目标检测入门知识二维目标检测必看论文 4 三维目标检测入门知识三维目标检测必熟悉数据集三维目标检测点云分类分割预备知识三维目标检测必熟悉&#xff0c;必跑通&am…

Node.js —— 前后端的身份认证 之用 express 实现 JWT 身份认证

JWT的认识 什么是 JWT JWT&#xff08;英文全称&#xff1a;JSON Web Token&#xff09;是目前最流行的跨域认证解决方案。 JWT 的工作原理 总结&#xff1a;用户的信息通过 Token 字符串的形式&#xff0c;保存在客户端浏览器中。服务器通过还原 Token 字符串的形式来认证用…

AIGC-风格迁移-“DEADiff:稳定可控的文本到图像风格化扩散模型 “-CVPR2024

DEADiff: An Efficient Stylization Diffusion Model with Disentangled Representations 代码&#xff1a;https://tianhao-qi.github.io/DEADiff/ 论文&#xff1a;https://arxiv.org/pdf/2403.06951 本文介绍了一种名为DEADiff的方法&#xff0c;旨在解决基于扩散的文本到图…

【机器学习论文阅读笔记】Robust Recovery of Subspace Structures by Low-Rank Representation

前言 终于要轮到自己汇报了好崩溃。。盯着论文准备开始做汇报ppt感觉一头乱麻&#xff0c;决定还是写博客理清思路再说吧 参考资料&#xff1a; 论文原文&#xff1a;arxiv.org/pdf/1010.2955 RPCA参考文章&#xff1a;RPCA - 知乎 (zhihu.com) 谱聚类参考文章&#xff1a…

Python使用pymysql操作数据库

大家好&#xff0c;当涉及到与数据库进行交互和操作时&#xff0c;Python的pymysql库是一个常用且功能强大的选择。pymysql提供了与MySQL数据库的连接、查询、插入、更新和删除等操作的方法&#xff0c;使得在Python中进行数据库操作变得简单而高效。 1、安装 pymysql 库 在开…