java之多线程篇

一、基本概念

1.什么是线程?

线程就是,操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。简单理解就是:应用软件中互相独立,可以同时运行的功能

2.什么是多线程?

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

3.多线程的作用?

提高效率

4.线程的应用场景?

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

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

二、并发和并行的概念

1.什么是并发?

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

2.什么是并行?

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

3.电脑不是只有一个CPU么,这个多个CPU同时执行的并行究竟是什么?

其实,CPU在市面有很多类型如下

比如2核4线程的CPU,就可以同时运行4个线程的任务。

三、多线程的实现方式(3种) 

1.继承Thread类的方式进行实现

用法:

1.定义一个类继承Thread类

2.这个类重写run方法

3.在main方法里面创建定义的类的对象

4.通过该对象的.start()方法启动线程

示例代码

public class ThreadDemo1 {public static void main(String[] args) {MyThread myThread1 = new MyThread();MyThread myThread2 = new MyThread();myThread1.setName("线程1");myThread2.setName("线程2");myThread1.start();myThread2.start();}
}
class MyThread extends Thread{@Overridepublic void run(){for(int i=0;i<100;i++){System.out.println(Thread.currentThread().getName()+" "+i);}}
}

上面的两个线程的代码run方法是同时执行的,并不会等一个线程的循环走完。

注意:线程类开启后执行的是run方法的代码 

2.实现Runnable接口的方式进行实现

用法:

1.自己定义一个类实现Runnable接口

2.重写里面的run方法

3.在main方法创建自己的类的对象

4.将定义的类传递给Thread构造方法创建一个Thread类的对象,并开启线程

示例 

public class ThreadDemo2 {public static void main(String[] args) {MyThread myThread = new MyThread();Thread thread1 = new Thread(myThread);Thread thread2 = new Thread(myThread);thread1.setName("线程1");thread2.setName("线程2");thread2.start();thread1.start();}
}
class MyThread implements Runnable{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+"-->"+i);}}
}

3.利用Callable接口和Future接口方式实现

前面两种实现方式,run方法没有返回值,不知道线程实现的结果,现在这个第三种方法是有返回值的。

用法:

1.创建一个类MyCallable实现callable接口

2.重写call(是有返回值的,表示多线程运行的结果)

3.创建MyCallable的对象(表示多线程要执行的任务)

4.传递MyCallable对象为参数创建FutureTask的对象(作用管理多线程运行的结果)

5.传递FutureTask对象为参数创建Thread类的对象,并启动(表示线程)

 示例

public class ThreadDemo3 {public static void main(String[] args) throws ExecutionException, InterruptedException {MyCallable mc = new MyCallable();FutureTask<Integer> ft = new FutureTask<>(mc);Thread t = new Thread(ft);t.start();System.out.println(ft.get());}
}
class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 0; i <= 100; i++) {sum += i;}return sum;}
}

结果输出5050

4.三种方式的比较 

四、Thread类的常用成员方法

1.七个方法

2.前四个细节

String getName()

void setName(String name)

细节:

        1. 果我们没有给线程设置名字,线程也是有默认的名字的

                格式:Thread-X(X序号,从0开始的)

        2.如果我们要给线程设置名字,可以用set方法进行设置,也可以构造方法设置

static Thread currentThread()

细节:

        当JVM虚拟机启动之后,会自动的启动多条线程

        其中有一条线程就叫做main线程

        他的作用就是去调用main方法,并执行里面的代码

        在以前,我们写的所有的代码,其实都是运行在main线程当中

static void sleep(long time)   

细节:

        1.哪条线程执行到这个方法,那么哪条线程就会在这里停留对应的时间

        2.方法的参数:就表示睡眠的时间,单位毫秒

                1秒 = 1000毫秒

        3.时间到了之后,线程会自动的醒来,继续执行下面的其他代码

 3.线程的优先级

(1)线程调度

抢占式调度:各条线程执行的顺序和时间是不确定的(随机)

非抢占式调度:各条线程执行的顺序是轮流执行和执行时间是差不多的

java中是选取第一种抢占式调度

(2)优先级(1~10)

抢占式调度就要涉及到线程的优先级越大,执行的顺序越前

于是可以通过Thread类的上面的两个成员方法来设置和获取线程的优先级

没有设置默认就是5

注意:这里的优先级是指优先级大的线程先执行的概率比较大,而不是百分百,比如说有两个一样的方法的线程,优先大的线程是有概率计较大的先执行完,但还是有小概率执行慢

 4.守护线程

当一个线程使用Thread类的etDaemon(boolean on)时,这个线程变成守护线程。

细节:

这个线程也可以叫做“备胎”线程,它将其他线程视为“女神”线程,当其他线程结束的时候,这个守护线程就会陆续结束,觉得自己没必要存在了,这就会可能导致守护线程的代码没有全部执行完。

应用场景

 上面聊天的场景,两个人聊天开两个线程,一个聊天,一个传输文件,如果聊天线程关闭了,就没有传输文件的必要了,于是将传输文件的线程设置为守护线程。

 5.出让/礼让线程

在当前线程的工作不重要时,将CPU资源让位给其他线程,通过使用Thread类的yield()方法来将当前资源让位给其他同优先级线程

public static void main(String[] args) {Thread t1 = new Thread(() -> {System.out.println("线程1开始运行!");for (int i = 0; i < 50; i++) {if(i % 5 == 0) {System.out.println("让位!");Thread.yield();}System.out.println("1打印:"+i);}System.out.println("线程1结束!");});Thread t2 = new Thread(() -> {System.out.println("线程2开始运行!");for (int i = 0; i < 50; i++) {System.out.println("2打印:"+i);}});t1.start();t2.start();
}

观察结果,我们发现,在让位之后,尽可能多的在执行线程2的内容。 

6.插入线程

当我们希望一个线程等待另一个线程执行完成后再继续进行,我们可以使用Thread类的join()方法来实现线程的插入。

public static void main(String[] args) {Thread t1 = new Thread(() -> {System.out.println("线程1开始运行!");for (int i = 0; i < 50; i++) {System.out.println("1打印:"+i);}System.out.println("线程1结束!");});Thread t2 = new Thread(() -> {System.out.println("线程2开始运行!");for (int i = 0; i < 50; i++) {System.out.println("2打印:"+i);if(i == 10){try {System.out.println("线程1加入到此线程!");t1.join();    //在i==10时,让线程1加入,先完成线程1的内容,再继续当前内容} catch (InterruptedException e) {e.printStackTrace();}}}});t1.start();t2.start();
}

线程2执行一半,线程1加入,先完成线程1的内容,再继续线程2的内容 

五、线程的生命周期

 六、线程的安全问题

1.售票代码引出问题

有100张票售卖,一共3个窗口在卖。

下面代码设置三个线程当作三个卖票的窗口,看看有什么问题

public class ThreadSafetyProblem {public static void main(String[] args) {MyThread1 myThread1 = new MyThread1();MyThread1 myThread2 = new MyThread1();MyThread1 myThread3 = new MyThread1();myThread1.setName("窗口1");myThread2.setName("窗口2");myThread3.setName("窗口3");myThread1.start();myThread2.start();myThread3.start();}
}
class MyThread1 extends Thread{//静态变量,几个线程共享private static int count = 0;public void run(){while(true){try {//这里只能try-catch丢给JVM,不能throwsThread.sleep(100);    //因为父类的run方法没有throws} catch (InterruptedException e) {throw new RuntimeException(e);}if (count < 100){System.out.println(Thread.currentThread().getName() + "正在卖第" + ++count+"张票");}else{break;}}}
}

 

 2.超卖和重复卖问题

运行后出现的问题结果如下图:

三个窗口在同时卖一张票,不合法!

超卖了,仅有100张票 

 

原因

线程的执行是有随机性的,cpu的执行权有可能被其他线程抢走。

线程1执行完票数的自增还没来得及打印的时候,线程2和线程3完成自增就会导致超卖和重复卖 

那么要怎么解决这个安全问题呢?下一个点就会讲到解决的方法之一——同步代码块。

 七、同步代码块

同步代码块的意思就是,把操作共享数据的代码锁起来

1.格式

synchronized(锁){

        操作共享数据的代码

}

 特点:

        1.锁默认打开,有一个线程进去了,锁自动关闭

        2.里面的代码全部执行完毕,线程出来,锁自动打开

2.修改后的售票代码 

class MyThread1 extends Thread{//静态变量,几个线程共享private static int count = 0;//锁对象,一定要是唯一的static Object obj = new Object();public void run(){while(true){//同步代码块synchronized (obj){try {Thread.sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}if (count < 2000){System.out.println(Thread.currentThread().getName() + "正在卖第" + ++count+"张票");}else {break;}}}}
}

运行后的结果,票正常售卖,从第一张卖到最后一张。 

3.细节问题

细节1:同步代码块不能放在while循环里面,因为放在外的话,一个线程拿到锁后就会必须把循环执行完,才会释放锁,这样的话一个线程就把票都卖完了。

细节2:锁的对象必须是唯一的,修改后的代码的锁是一个Object对象,用static修饰后表示全局共享唯一的对象。也可以使用MyThread.class表示唯一的字节码文件对象

八、同步方法 

如果我们要锁的代码是整个方法,这个时候就要用到同步方法了

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

1.格式

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

2.特点

特点1:同步方法是锁住方法里面所有的代码

特点2:锁对象不能自己指定,如果方法是非静态的,锁对象就是方法所在类对象this

                                                 如果方法是静态的,锁对象就是当前类的字节码文件对象

3.使用同步方法解决售票问题 

示例代码

这段代码与前面不同的是,这段代码是使用实现Runnable接口实现的多线程,只需要创建一个实现Runnable接口的javabean类的对象,所以票数这个变量的内存地址是唯一的,所以不用像上面的代码一样用static修饰票数count

注意:下面同步方法是非静态的,所以锁对象就是MyRunnable类的对象mr

public class ThreadSafetyProblem {public static void main(String[] args) {MyRunnable mr = new MyRunnable();Thread myThread1 = new Thread(mr);Thread myThread2 = new Thread(mr);Thread myThread3 = new Thread(mr);myThread1.setName("窗口1");myThread2.setName("窗口2");myThread3.setName("窗口3");myThread1.start();myThread2.start();myThread3.start();}
}
class MyRunnable implements Runnable{int count = 0;public void run(){while(true){//同步代码块if (method()) break;}}//锁对象是thisprivate synchronized boolean method() {try {Thread.sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}if (count < 2000){System.out.println(Thread.currentThread().getName() + "正在卖第" + ++count+"张票");}else {return true;}return false;}
}

4.StringBuffer为什么是线程安全的?

虽然StringBuilder和StringBuffer的成员方法是一样的,但为什么之前建议在多线程的情况下使用StringBuffer?

这是由于StringBuffer的成员方法比较 StringBuilder多了一个Sychronized修饰词,保证了线程的安全,但是在单线程的情况下,还是使用StringBuilder好一些。

九、Lock锁

 

1.售票代码使用手动上锁 

public class ThreadSafetyProblem {public static void main(String[] args) {MyThread1 myThread1 = new MyThread1();MyThread1 myThread2 = new MyThread1();MyThread1 myThread3 = new MyThread1();myThread1.setName("窗口1");myThread2.setName("窗口2");myThread3.setName("窗口3");myThread1.start();myThread2.start();myThread3.start();}
}
class MyThread1 extends Thread{//静态变量,几个线程共享private static int count = 0;//创建一个锁对象 用static修饰表示共享唯一Lock lock = new ReentrantLock();public void run(){while(true){lock.lock();try {Thread.sleep(10);if (count < 2000){System.out.println(Thread.currentThread().getName() + "正在卖第" + ++count+"张票");}else {break;}} catch (InterruptedException e) {throw new RuntimeException(e);} finally {lock.unlock();}}}
}

十、死锁

1.示例代码

下面的代码用了锁的嵌套

public class MyThread extends Thread {static Object objA = new Object();static Object objB = new Object();@Overridepublic void run() {//1.循环while (true) {if ("线程1".equals(getName())) {synchronized (objA) {System.out.println("线程1拿到了A锁,准备拿B锁");//线程1卡在这里synchronized (objB) {System.out.println("线程1拿到了B锁,顺利执行完一轮");}}} else if ("线程2".equals(getName())) {if ("线程2".equals(getName())) {synchronized (objB) {System.out.println("线程2拿到了B锁,准备拿A锁");//线程2卡在这里synchronized (objA) {System.out.println("线程2拿到了A锁,顺利执行完一轮");}}}}}}
}

 运行结果

显然两个线程都卡在第一层同步代码的锁那里,程序结束不了。

2.如何解决 

很简单,不要写锁或同步代码块的锁嵌套就行。

十一、生产者和消费者模式

生产者消费者模式是一个十分经典的多线程协作的模式,就是要打破两个线程随机执行的规则,你一次我一次。

1.流程框图(等待唤醒机制)

2.涉及的方法

 调用 wait() 方法的线程会释放它持有的锁,并进入等待状态,直到它被其他线程通过调用 notify() 或 notifyAll() 唤醒。

3.步骤 

消费者和生产者的代码都按下面的步骤走:

1.循环

2.同步代码块(给消费者或生产者上锁)

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

4.判断共享数据是否到了末尾(没有到末尾,执行(消费者或生产者的)核心逻辑)

4. 示例代码

(1)桌子代码

public class Desk {/*桌子的作用:控制生产者和消费者的执行*/// 判断桌子上有没有食物// 0 没有食物 生产者的线程执行// 1 有食物   消费者的线程执行//这里一般不用boolean类型,因为boolean类型只能是true或者false//如果有多条线程,int 可以表示多条线程的状态public static int foodFlat = 0;// 消费者现在所能吃食物的碗数public static int count = 10;//锁对象static Object lock = new Object();
}

(2)消费者代码

public class Foodie extends Thread {/*1.循环2.同步代码块(给消费者或生产者上锁)3.判断共享数据是否到了末尾(到了末尾)4.判断共享数据是否到了末尾(没有到末尾,执行(消费者或生产者的)核心逻辑)*/public void run(){while(true){synchronized (Desk.lock){if (Desk.count == 0){//吃不下了,直接结束break;}else {//如果桌上没有食物if (Desk.foodFlat == 0){try {//进入等待Desk.lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}else {//桌上有食物Desk.foodFlat = 0;Desk.count--;System.out.println("消费者吃掉了一碗面条,还能吃"+Desk.count+"碗");//通知唤醒厨师Desk.lock.notify();}}}}}
}

(3)生产者代码

public class Cook extends Thread {public void run(){while(true){synchronized (Desk.lock){//判断消费者是否吃饱了if (Desk.count==0){//吃饱了就结束break;}else {//消费者还能吃//判断桌子上还有食物么//有食物就进入等待if (Desk.foodFlat==1){try {Desk.lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}else {//没食物//厨师做一碗面条Desk.foodFlat = 1;System.out.println("厨师做了一碗面条");//做好就唤醒消费者Desk.lock.notify();}}}}}}

(4)main方法

public class Test {public static void main(String[] args) {Desk desk = new Desk();Foodie foodie = new Foodie();Cook cook = new Cook();foodie.setName("吃货");cook.setName("厨师");foodie.start();cook.start();}
}

 (5)执行结果

从运行结果可以看出来,消费者和生产者模式可以使多个线程不再随机而是按顺序的来执行。 

 5.阻塞队列实现唤醒机制

 (1)成员方法put和take

put方法底层原理

放入一个数据,put方法接收数据,先使当前线程获得锁(这个队列的),唤醒等待的线程,如果队列满了,当前线程进入等待,释放当前锁。

 take方法底层原理

take方法取出数据,先使当前线程获得锁(这个队列的),唤醒等待的线程,如果队列空的,当前线程进入等待,释放当前锁。

(2)代码实现

用阻塞队列实现消费者和生产者的示例代码 

消费者代码

public class Foodie extends Thread {ArrayBlockingQueue<String> queue;public Foodie(ArrayBlockingQueue<String> queue){this.queue = queue;}public void run() {while(true){try {queue.take();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("吃货吃了一碗面条");}}
}

生产者代码 

public class Cook extends Thread{ArrayBlockingQueue<String> queue;public Cook(ArrayBlockingQueue<String> queue) {this.queue = queue;}public void run() {while(true){try {queue.put("一碗面");} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("厨师做了一碗面条");}}
}

main方法 

public class Test {public static void main(String[] args) {ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);Foodie f = new Foodie(queue);Cook c = new Cook(queue);f.setName("吃货");c.setName("厨师");f.start();c.start();}
}

运行结果

 

看到上面的结果有人会认为,厨师连续做了两碗面,是不是违反了模式了?

其实不然,消费者和生产者两条线程还是一条一次轮流执行的,重复输出是因为输出的代码放在了锁的外面,所以两个线程是随机的,抢着输出的,厨师做了多少碗面和消费者吃了多少碗的数量还是一样的。厨师放一碗面到队列,吃货就拿一碗。

注意:锁只在阻塞队列里面,即示例代码的put和take方法里面

 

 十二、线程的6种状态

 严格的来说,线程有7种状态,但是线程在进入要运行阶段的时候,JVM直接将线程丢给操作系统,java就不管了。所以对于java来说,线程是有6种状态。如下,

 

十三、Demo 

1.Demo1

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

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

public class MyRunnable implements Runnable{int count = 100;@Overridepublic void run() {while (true) {synchronized (this) {if (count < 10){break;}else {count--;System.out.println(Thread.currentThread().getName() + "送出一份礼物,剩余" + count + "份礼物");}}}}
}
public class Test {public static void main(String[] args) {MyRunnable mr = new MyRunnable();Thread t1 = new Thread(mr);Thread t2 = new Thread(mr);t1.setName("小明");t2.setName("小红");t1.start();t2.start();}
}

2.Demo2

 

public class MyRunnable implements Runnable{double money = 100;int count = 3;Random rnd = new Random();public void run(){synchronized (this) {if (count == 0){System.out.println(Thread.currentThread().getName()+"没抢到");}else if (count == 1){count--;System.out.println(Thread.currentThread().getName()+"抢到了"+money+"元");money = 0;}else {double get = (rnd.nextDouble()*money);System.out.println(Thread.currentThread().getName()+"抢到了"+get+"元");money = money - get;count--;}}}
}
public class Test {public static void main(String[] args) {MyRunnable mr = new MyRunnable();Thread t1 = new Thread(mr);Thread t2 = new Thread(mr);Thread t3 = new Thread(mr);Thread t4 = new Thread(mr);Thread t5 = new Thread(mr);t1.setName("玩家1");t2.setName("玩家2");t3.setName("玩家3");t4.setName("玩家4");t5.setName("玩家5");t1.start();t2.start();t3.start();t4.start();t5.start();}
}

3.Demo3

public class MyRunnable implements Runnable{//奖池ArrayList<Integer> list;public MyRunnable(ArrayList<Integer> list) {this.list = list;}@Overridepublic void run() {while (true){synchronized (this) {if (list.isEmpty()){break;}Collections.shuffle(list);System.out.println(Thread.currentThread().getName()+"又产生了一个"+list.get(0)+"元大奖");list.remove(0);}//在锁外面休眠一会,这样另外一个线程就先执行,输出会好看一点try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
public class Test {public static void main(String[] args) {ArrayList<Integer> list = new ArrayList<>();Collections.addAll(list,10,5,20,50,100,200,500,800,2,80,300,700);MyRunnable mr = new MyRunnable(list);Thread t1 = new Thread(mr);Thread t2 = new Thread(mr);t1.setName("抽奖箱1");t2.setName("抽奖箱2");t1.start();t2.start();}
}

4.Demo4 (多线程统计并求最大值)

public class MyRunnable implements Runnable{//奖池ArrayList<Integer> list;public MyRunnable(ArrayList<Integer> list) {this.list = list;}@Overridepublic void run() {//定义一个集合,收集每一个抽奖箱每次中奖的金额ArrayList<Integer> moneys = new ArrayList<>();while (true){synchronized (this) {if (list.isEmpty()){Collections.sort(moneys);System.out.println("在此次抽奖过程中,"+Thread.currentThread().getName()+"总共产生了"+moneys.size()+"个奖项");String string = moneys.toString();System.out.println("\t分别为:"+string.substring(1,string.length()-1)+"最高奖项为"+moneys.get(moneys.size() - 1)+",总计额为"+moneys.stream().mapToInt(Integer::intValue).sum());break;}Collections.shuffle(list);moneys.add(list.get(0));list.remove(0);}try {Thread.sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
public class Test {public static void main(String[] args) {ArrayList<Integer> list = new ArrayList<>();Collections.addAll(list,10,5,20,50,100,200,500,800,2,80,300,700);String name1 = "抽奖箱1";String name2 = "抽奖箱2";MyRunnable mr = new MyRunnable(list);Thread t1 = new Thread(mr);Thread t2 = new Thread(mr);t1.setName(name1);t2.setName(name2);t1.start();t2.start();}
}

5.Demo5(多线程之间的比较)

由于这里要多线程之间进行比较,所以必须要有返回值,run方法没有返回值,所以使用第三种方法实现多线程,即实现Callable接口和Future接口 

public class MyCallable implements Callable<Integer> {//奖池ArrayList<Integer> list;public MyCallable(ArrayList<Integer> list) {this.list = list;}@Overridepublic Integer call(){//定义一个集合,收集每一个抽奖箱每次中奖的金额ArrayList<Integer> moneys = new ArrayList<>();while (true){synchronized (this) {if (list.isEmpty()){Collections.sort(moneys);System.out.println("在此次抽奖过程中,"+Thread.currentThread().getName()+"总共产生了"+moneys.size()+"个奖项");String string = moneys.toString();System.out.println("\t分别为:"+string.substring(1,string.length()-1)+"最高奖项为"+moneys.get(moneys.size() - 1)+",总计额为"+moneys.stream().mapToInt(Integer::intValue).sum());break;}Collections.shuffle(list);moneys.add(list.get(0));list.remove(0);}try {Thread.sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}}return moneys.get(moneys.size() - 1);}
}
public class Test {public static void main(String[] args) throws ExecutionException, InterruptedException {ArrayList<Integer> list = new ArrayList<>();Collections.addAll(list,10,5,20,50,100,200,500,800,2,80,300,700);String name1 = "抽奖箱1";String name2 = "抽奖箱2";MyCallable mc = new MyCallable(list);FutureTask<Integer> ft1 = new FutureTask<>(mc);FutureTask<Integer> ft2 = new FutureTask<>(mc);Thread t1 = new Thread(ft1,name1);Thread t2 = new Thread(ft2,name2);t1.start();t2.start();if (ft1.get()>ft2.get()){System.out.println("在此次抽奖过程中,"+name1+"中产生了最大奖项,该奖项金额为"+ft1.get()+"元");}else {System.out.println("在此次抽奖过程中,"+name2+"中产生了最大奖项,该奖项金额为"+ft2.get()+"元");}}
}

十四、多线程的内存图 

十五、线程池

 1.核心原理

(1)创建一个池子,池子中是空的

(2)提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子

        下回再次提交任务时,不需要创建新的线程,直接复用已有的线程即可

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

 

2.操作步骤

(1)创建线程池对象

(2)提交任务(提交线程)

(3)任务都执行完毕后,关闭线程池

注意:一般的服务器线程池是不会关闭的,比如王者游戏24小时都能玩

3.自定义线程池

把一个餐厅的运营的七大核心因素看作线程的参数

(1)两种情况+三个临界点

A.线程池的参数如下,提交的任务数小于3+3+3,因此不会触发任务过多解决方案,8个任务从核心线程开始放,然后放队伍,发现不够放了,于是找来临时工(临时线程)放多余的两个任务。

注意:任务并不是先放就先执行,比如下面任务7,8后放比在排队的4,5,6先走。

B.下面这种情况和上面不同的是,提交的任务数量超过了3+3+3,于是最后一个任务触发了任务拒绝策略。 其他和上面相同

 

三个临界点 

 (2)任务拒绝策略

 

(3)创建一个线程池

构造方法和参数如下

使用这个线程池就提交任务就行pool.submit(线程任务)

(4)最大并行数 

在多线程编程中,指同时运行的线程数量的上限。

下面代码可以获得java可用的处理器的数目,即可同时运行线程的最大数量

(5)线程池多大合适

 

 cpu计算时间和等待时间可以通过插件thread dump计算统计

 

 

 

 

        

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

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

相关文章

无人机之飞行控制系统篇

一、飞行控制系统组成 包括惯性测量单位、GPS接收机、气压高度计、空速计等传感器&#xff0c;以及飞控计算机、伺服作动器等设备。 二、飞行控制原理 通过传感器实时感知无人机的飞行状态&#xff0c;将数据传输给飞控计算机进行处理&#xff0c;计算机再根据预设的飞行计划和…

13-按键的元件模型创建

1.画线的时候&#xff0c;栅格切为10mil 2.放置管脚的时候&#xff0c;栅格切为100mil

开发框架DevExpress XAF v24.2产品路线图预览——增强跨平台性

DevExpress XAF是一款强大的现代应用程序框架&#xff0c;允许同时开发ASP.NET和WinForms。XAF采用模块化设计&#xff0c;开发人员可以选择内建模块&#xff0c;也可以自行创建&#xff0c;从而以更快的速度和比开发人员当前更强有力的方式创建应用程序。 DevExpress XAF是一…

LLaMA- Adapter V2: Parameter-Efficient Visual Instruction Model

发表时间&#xff1a;28 Apr 2023 论文链接&#xff1a;https://arxiv.org/pdf/2304.15010 作者单位&#xff1a; Shanghai Artificial Intelligence Laboratory Motivation&#xff1a;如何有效地将大型语言模型 (LLM) 转换为指令追随者最近是一个流行的研究方向&#xff0…

Linux基于centOS7【内存与OS的随谈】,进程初学【PCB】【fork】【进程排队】

冯诺依曼体系结构——存储器 存储器主要指的是内存&#xff0c;它有个特点就是掉电易失 磁盘等其它输入和输出设备 为什么要在计算机体系结构中要存在内存 我们知道&#xff0c;CPU的处理速度很快很快&#xff0c;但输入设备&#xff0c;以及输出设备&#xff0c;是相对很慢的…

sql注入靶场搭建

1.安装小皮面板&#xff08;PhpStudy&#xff09; 1.从官网下载&#xff1a;http://www.xp.cn 2、Sqli-labs环境安装 准备好sqli-labs-php7-master文件 3.安装之前确保本地没有下载mysql服务器 如果电脑下载了MySQL可以把MySQL的服务停掉 此电脑>右键>管理>服务…

QModbus例程分析

由于有一个Modebus上位机的需要&#xff0c;分析一下QModbus Slave的源代码&#xff0c;方便后面的开发。 什么是Modbus Modbus是一种常用的串行通信协议&#xff0c;被广泛应用于工业自动化领域。它最初由Modicon&#xff08;目前属于施耐德电气公司&#xff09;于1979年开发…

C++:vector容器

概览 std::vector是C标准模板库(STL)中的一种动态数组容器。它提供了一种类似于数组的数据结构&#xff0c;但是具有动态大小和更安全的内存管理。 定义和基本特性 std::vector是C标准库中的一 个序列容器&#xff0c;它代表了能够动态改变大小的数组。与普通数组一样&#x…

模拟面试题1

目录 一、JVM的内存结构&#xff1f; 二、类加载器分为哪几类&#xff1f; 三、讲一下双亲委派机制 为什么要有双亲委派机制&#xff1f; 那你知道有违反双亲委派的例子吗&#xff1f; 四、IO 有哪些类型&#xff1f; 五、Spring Boot启动机制 六、Spring Boot的可执行…

关于儿童编程语言

青少年通常会通过Scratch或Python开始学习编程。在这两种语言中&#xff0c;代码的编写&#xff08;或者在Scratch中是构建&#xff09;方式类似于英语&#xff0c;这使得初学者更容易学习。Scratch的一个重要卖点是对视觉和运动感知学习者非常友好。这些代码块按颜色编码&…

最短路问题中的bellman-ford算法

最短路问题中的bellman-ford算法 题目 如果要处理单源最短路问题当中存在负权边的&#xff0c;那么就需要用到 bellman-ford算法和SPFA算法&#xff0c;一般情况下都是用 SPFA算法&#xff0c;除了有边数限制的情况只能用bellman-ford算法&#xff0c;比如下面这种 题目 给定…

混合密度网络Mixture Density Networks(MDN)

目录 简介1 介绍2 实现3 几个MDN的应用&#xff1a;参考 简介 平方和或交叉熵误差函数的最小化导致网络输出近似目标数据的条件平均值&#xff0c;以输入向量为条件。对于分类问题&#xff0c;只要选择合适的目标编码方案&#xff0c;这些平均值表示类隶属度的后验概率&#x…

《程序猿入职必会(10) · SpringBoot3 整合 MyBatis-Plus》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

Linux中DHCP服务器配置和管理

文章目录 一、DHCP服务1.1、DHCP的工作流程1.2、DHCP的工作模式1.3、dhcp的主要配置文件 二、安装DHCP服务2.1、更新yum源2.2、安装DHCP服务软件包2.3、配置DHCP服务2.4、启用DHCP服务&#xff08;解决报错&#xff09;2.4.1、查看dhcpd服务的状态和最近的日志条目2.4.2、查看与…

【网络安全】探索AI 聊天机器人工作流程实现RCE

未经许可,不得转载。 文章目录 前言正文前言 我发现了一个广泛使用的AI聊天机器人平台中的远程代码执行漏洞。该漏洞存在于聊天机器人的自定义工作流响应代码中,这些工作流允许开发人员通过创建定制的流程来扩展机器人的功能。 正文 在浏览自动化聊天机器人的多个特定功能…

AI界的“小钢炮“:MiniCPM-V 2.6 版本震撼发布!

MiniCPM-V 2.6 面壁智能推出了一款颠覆性的端侧AI多模态模型——MiniCPM-V 2.6。这个被亲切地称为"小钢炮"的模型&#xff0c;以其惊人的性能和极致的效率&#xff0c;向业界巨头发起了挑战。 MiniCPM-V 2.6 MiniCPM-V 2.6 是 MiniCPM-V 系列中最新、性能最佳的模型。…

算法板子:匈牙利算法——二分图的最大匹配

目录 1. 基础概念 &#xff08;1&#xff09;二分图的概念 &#xff08;2&#xff09; 匈牙利算法的作用 2. 代码 1. 基础概念 &#xff08;1&#xff09;二分图的概念 顶点集 V 分为两个集合&#xff0c;且图中每条边依附的两个顶点都分属于这两个子集&#xff0c;也就是第…

《UniverSeg: Universal Medical Image Segmentation》ICCV2023

摘要 这篇论文提出了一种名为 UniverSeg 的方法&#xff0c;它能够解决未见过的医学图像分割任务&#xff0c;而无需额外的训练。现有的深度学习模型通常无法泛化到新的解剖结构、图像模式或标签上。UniverSeg 利用一种新的 CrossBlock 机制&#xff0c;通过查询图像和定义新分…

倍福PLC数据 转 CCLink IE Field Basic项目案例

目录 1 案例说明 1 2 VFBOX网关工作原理 1 3 准备工作 2 4 设置倍福PLC 2 5 配置网关参数采集倍福PLC数据 4 6 使用CCLINK协议转发数据 7 7 三菱PLC连接网关的CCLINK的设置 8 8 案例总结 12 1 案例说明 设置倍福PLC&#xff0c;开通ADS通信设置网关采集倍福PLC数据把采集的数…

小巧免费的笔记本电池检测工具

BatteryInfoView是一款免费的笔记本电池检测软件&#xff0c;适用于笔记本电脑和上网本。该软件能够提供电池的详细信息&#xff0c;包括电池名称、制造商名称、序列号、制造日期、电源状态&#xff08;充电/放电&#xff09;、当前电池容量、完全充电容量、设计容量、充电放电…