Thread多线程(创建,方法,安全,通信,线程池,并发,并行,线程的生命周期)【全详解】

目录

1.多线程概述

2.多线程的创建

3.Thread的常用方法

4.线程安全

5.线程同步

6.线程通信

7.线程池

8.其它细节知识:并发、并行

9.其它细节知识:线程的生命周期   


1.多线程概述

        线程是什么?

                线程(Thread)是一个程序内部的一条执行流程。

                程序中如果只有一条执行流程,那这个程序就是单线程的程序。

        多线程是什么?

                多线程是指从软硬件上实现的多条执行流程的技术(多条线程由CPU负责调度执行)。

2.多线程的创建

        方式一:继承Thread类

        实现步骤:

                定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法

                创建MyThread类的对象

                调用线程对象的start()方法启动线程(启动后还是执行run方法的)

        代码实现

        

package com.itheima.day11.teacher.thread01;
/*start()  开启新线程的方法   ---  线程开启功能run()  线程执行的代码       ---  线程任务继承方法MyThread 既是一个线程对象(负责线程开启)  又是一个线程任务(写需要执行线程任务)耦合性高!!线程对象 -- 线程任务 分离!!*/
public class MyThread extends Thread{@Overridepublic void run() {/*每个新的线程 都循环输出 十次*/for (int i = 0; i < 10; i++) {// 代码执行过程中 可以获取到当前正在运行的线程对象Thread thread = Thread.currentThread();System.out.println("新的线程"+thread.getName()+"正在执行"+i);}}
}-----------------------
package com.itheima.day11.teacher.thread01;public class Test {public static void main(String[] args) {System.out.println("当前是一个程序的入口"+Thread.currentThread().getName());System.out.println("程序入口也是一个线程 这个线程叫主线程..");//主线程执行过程中 再产生新的线程。/*线程的创建方式一1: 创建一个类 继承 Thread线程类2: 手动重写run方法。----就是新的线程对象要执行的内容.3: 创建 子类对象(线程对象)。4: 调用start方法 开启这个新的线程。当开启完了 程序中出现两个线程了 一个是 主线程  一个新建的线程。每个线程将来都是独立的空间。*/MyThread t1 = new MyThread();t1.start();// 开启新的线程for (int i = 0; i < 10; i++) {System.out.println("线程"+Thread.currentThread().getName()+"正在执行"+i);}}
}--------------------
package com.itheima.day11.teacher.thread01;public class Test2 {/*线程的创建方式一1: 创建一个类 继承 Thread线程类2: 手动重写run方法。----就是新的线程对象要执行的内容.3: 创建 子类对象(线程对象)。4: 调用start方法 开启这个新的线程。当开启完了 程序中出现两个线程了 一个是 主线程  一个新建的线程。每个线程将来都是独立的空间。多个线程执行每次执行效果 不尽相同 因为 CPU的高速切换 没有规律Thread 代表线程对象的类Thread.currentThread() 获取 执行当前代码的线程。普通方法getName() 获取线程的名字 名字默认 Thread-0  Thread-1 ....可以设置名字setName("...")*/public static void main(String[] args) {//程序入口也是一个线程 这个线程叫主线程..System.out.println("当前是一个程序的入口"+Thread.currentThread().getName());//主线程执行过程中 再产生新的线程。MyThread t1 = new MyThread();t1.setName("小迪迪");t1.start();// 开启新的线程// 这还是main中for (int i = 0; i < 10; i++) {System.out.println("线程"+Thread.currentThread().getName()+"正在执行"+i);}}
}

                优缺点:

                        优点:编码简单

                        缺点:存在单继承的局限性,线程类继承Thread后,不能继承其他类,不便于扩展

        方式二:实现Runnable接口

                实现步骤:

                        定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法

                        创建MyRunnable任务对象

                        把MyRunnable任务对象交给Thread处理。

                        调用线程对象的start()方法启动线程

package com.itheima.day11.teacher.thread02;
/*线程任务类 里面只有线程任务 run方法*/
public class    MyRunnable implements Runnable{@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println("当前在"+Thread.currentThread().getName()+"正在输出:"+i);}}
}-------------------------
package com.itheima.day11.teacher.thread02;public class RunnableTest {/*创建线程方式二1: 定义一个实现Runnable接口的 线程任务类,重写run方法。2: 创建一个线程任务对象。3: 创建线程对象 并在构造中传递线程任务对象。4: 开启新的线程 线程对象.start()*/public static void main(String[] args) {// 创建线程任务对象MyRunnable mr = new MyRunnable();// 创建线程对象的同时 将线程任务传递过去 --- 线程和任务绑定Thread t = new Thread(mr);//调用start方法t.start();//主线程操作for (int i = 0; i <10 ; i++) {System.out.println("当前在"+Thread.currentThread().getName()+"正在输出:"+i);}}
}--------------------
package com.itheima.day11.teacher.thread02;public class RunnableTest2 {/*创建线程方式二1: 定义一个实现Runnable接口的 线程任务类,重写run方法。2: 创建一个线程任务对象。3: 创建线程对象 并在构造中传递线程任务对象。4: 开启新的线程 线程对象.start()*/public static void main(String[] args) {// 创建线程任务对象MyRunnable mr = new MyRunnable();//上面的操作 创建了一个外部类 实现了接口 并创建该外部类的对象  外部类对象---Runnabel接口的实现类对象// 创建线程对象的同时 将线程任务传递过去 --- 线程和任务绑定Thread t1 = new Thread(mr);//mr 是 Runnable接口的实现类对象//调用start方法t1.start();//
//        Thread t2  = new Thread(匿名内部类形式)  匿名内部类本质  子类对象Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println("当前在"+Thread.currentThread().getName()+"正在输出:"+i);}}});t2.start();// 能不能用lambda  可以// lambda  作用简化匿名内部类   使用前提 参数是一个函数式接口  函数式接口 有且只有一个抽象方法的接口new Thread(()->{for (int i = 0; i < 10; i++) {System.out.println("当前在"+Thread.currentThread().getName()+"正在输出:"+i);}}).start();}
}

                优缺点:

                        优点:任务类只是实现接口,可以继续继承其他类、实现其他接口,扩展性强。 

                        缺点:需要多一个Runnable对象。

        方式三:实现Callable接口  

        实现步骤:

        1.创建任务对象:

                1.定义一个类实现Callable接口,重写call方法,封装要做的事情和要返回的数据。        

                2.把Callable类型的对象封装成FutureTask对象(线程任务对象)。

         2.把线程任务对象封装成Thread对象。

         3.调用Thread对象的start方法启动线程。

         4.线程执行完毕后、通过FutureTask对象的的get方法去获取线程任务执行的结果。

 

package com.itheima.day11.teacher.thread03;import java.util.concurrent.Callable;/*Callable接口有个泛型 表示 返回的结果类型。返回值 call*/
public class MyCallable implements Callable<Integer> {// 构造   方法 都可以访问 成员变量private Integer number;public MyCallable(Integer number){// 当能够调用call方法的时候  构造方法执行完了// 方法中没有参数  构造中可不可以设计参数// 把构造中传递的number 赋值给成员变量this.number = number;}//任务 求一个数的 绝对值@Overridepublic Integer call() throws Exception {System.out.println("当前的线程:"+Thread.currentThread().getName());return Math.abs(number);//直接使用}}-----------------------
package com.itheima.day11.teacher.thread03;import java.util.concurrent.Callable;/*Callable接口有个泛型 表示 返回的结果类型。返回值 call*/
public class MyCallable1 implements Callable<String> {//任务 求一个数的 绝对值@Overridepublic String call() throws Exception {System.out.println("当前的线程:"+Thread.currentThread().getName());return "下载任务执行成功";//直接使用}
}-----------------------
package com.itheima.day11.teacher.thread03;import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class CallableTest {/*创建线程方式三1:创建子类实现Callable接口 完成call方法重写。2:创建线程任务(自定义callable)对象.3:创建一个 线程任务管理对象 FutureTask封装 callable实现对象。4:创建线程对象 绑定线程任务管理对象5:调用start方法*/public static void main(String[] args) throws ExecutionException, InterruptedException {//创建线程任务对象MyCallable my = new MyCallable(-10);//带参构造//传参给 Thread 创建线程对象
//        Thread thread = new Thread(my);// Callable 是一个带有返回值的 任务--需要先交给任务管理对象 因为执行之后该线程是有返回值的 得有对象去处理返回值FutureTask<Integer> task = new FutureTask<>(my);// 在把 task对象 绑定到线程对象中Thread thread = new Thread(task);// 调用start方法thread.start();// 返回值在 Task身上System.out.println("任务执行结果:"+task.get());MyCallable1 callable1 = new MyCallable1();FutureTask<String> stringFutureTask = new FutureTask<String>(callable1);Thread thread1 = new Thread(stringFutureTask);thread1.start();String s = stringFutureTask.get();System.out.println(s);//这样写没意义,不能传参数,只能自己定义吗  
/*        FutureTask<Integer> integerFutureTask = new FutureTask<>(new Callable<Integer>() {int a=10;int b=0;@Overridepublic Integer call() throws Exception {return a+b;}});new Thread(integerFutureTask).start();  //不执行线程是拿不到返回会值结果的
//        new Thread(new FutureTask<Integer>(()-> 10+0 )).start(); 可以这样简写,但是拿不到结果值没意义System.out.println(integerFutureTask.get());*/}
}

3.Thread的常用方法

    

package com.itheima.day12.teacher.thread01;public class MyThread extends Thread{public MyThread(String name){super(name);//把名字传递给父类的构造}//线程任务方法 将来哪个线程对象执行 在它的代码中就可以得到哪个线程对象@Overridepublic void run() {// 干吕布Thread thread = Thread.currentThread();//获取当前线程对象for (int i = 1; i <=5 ; i++) {//每人跟吕布过招 5次System.out.println(thread.getName()+"大呼:吕布小儿,莫跑~~吃我"+i+"招!!");
//            System.out.println(super.getName()+"大呼:吕布小儿,莫跑~~吃我"+i+"招!!");}}
}----------------------
package com.itheima.day12.teacher.thread01;public class ThreadDemo {public static void main(String[] args) throws InterruptedException {//三英战吕布System.out.println("接下来请欣赏  三英战吕布!");// 每秒钟 输出  5  4  3  2  1for (int i = 5; i >=1 ; i--) {System.out.println(i);//休眠一秒Thread.sleep(1000);}MyThread bb = new MyThread("刘备");System.out.println(bb.getName());//        bb.setName("刘备");MyThread yy = new MyThread("关羽");System.out.println(yy.getName());MyThread ff = new MyThread("张飞");System.out.println(ff.getName());bb.start();yy.start();ff.start();}
}---------------------
package com.itheima.day12.teacher.thread01;public class ThreadDemo02 {public static void main(String[] args) throws InterruptedException {System.out.println("宇迪和弓箭手 一起打吕布");//创建宇迪线程MyThread my = new MyThread("宇迪");my.start();//宇迪线程执行my.join();//先执行 当前 my线程对象 再执行其他的线程//同时总部 排除弓箭手也对 宇迪进行打击Thread.currentThread().setName("弓箭手");for (int i = 1; i <= 5; i++) {System.out.println(Thread.currentThread().getName()+"在发射第"+i+"只箭~");}}
}

4.线程安全

        什么是线程安全问题?

                多个线程,同时操作同一个共享资源的时候,可能会出现业务安全问题。

        线程安全问题出现的原因?

                1.存在多个线程在同时执行

                2.多个线程同时访问一个共享资源

                3.存在修改该共享资源的情况

        用程序模拟线程安全问题

/**
出现的问题两人同时取钱,卡里10万,结果都取成功,卡里-10万
*/package com.itheima.day12.teacher.asynchronize;
// 先定义账户类
public class Account {private String cardId;private double money;//余额public Account() {}public Account(String cardId, double money) {this.cardId = cardId;this.money = money;}/*设计一个取钱的方法参数   取得钱 金额返回值  不需要*/public void drawMoney(double money){//  局部位置money 代表取的钱//  成员位置money 代表余额//  this.money -= money;// 知道是谁来取的----哪个线程执行到这里了 谁就是哪个线程String name = Thread.currentThread().getName();// 判断余额是否充足if(this.money >= money){//可以取System.out.println(name+"来取钱:"+money+" 成功!");this.money -= money;System.out.println(name+"取钱之后的余额:"+this.money);}else {System.out.println(name+"来取钱:余额不足,请充值后再去...");}}/*** 获取* @return cardId*/public String getCardId() {return cardId;}/*** 设置* @param cardId*/public void setCardId(String cardId) {this.cardId = cardId;}/*** 获取* @return money*/public double getMoney() {return money;}/*** 设置* @param money*/public void setMoney(double money) {this.money = money;}@Overridepublic String toString() {return "Account{cardId = " + cardId + ", money = " + money + "}";}
}---------------
package com.itheima.day12.teacher.asynchronize;
/*取钱线程类*/
public class DrawMoney extends Thread{// 定义一个账户的成员变量  --初始化交给了构造 由外界传递进来private Account account;//  构造第一个参数 是 取钱的账户对象  第二参数 表示线程的名字public DrawMoney(Account account,String name){super(name);this.account = account;}@Overridepublic void run() {//取钱就是在账户里面 减少余额// 调用账户对象 的 取钱方法// 小明线程 小红线程 操作的 账户是同一个// 在测试类创建 一个账户 传递进来account.drawMoney(100000); // 通过账户对象 取钱 (取的钱数)}
}------------------
package com.itheima.day12.teacher.asynchronize;public class ThreadTest {public static void main(String[] args) {//创建一个账户对象   小明和小红 共享账户   卡号        余额Account account = new Account("ICBC-114",100000);//创建两个线程对象 分别代表小红 和 小明// 线程对象中传递 共享的账户 以及 线程名字new DrawMoney(account,"小明").start();new DrawMoney(account,"小红").start();}
}

5.线程同步

        1.认识线程同步: 线程同步是解决线程安全问题的方案

        2.线程同步的思想:

                1.让多个线程实现先后依次访问共享资源,这样就解决了安全问题。

                2.加锁:每次只允许一个线程加锁,加锁后才能进入访问,访问完毕后自动解锁,然后其他线程才能再加锁进来。

        3.线程同步的解决方案

                方式一:同步代码块

                        作用:把访问共享资源的核心代码给上锁,以此保证线程安全。

                        原理:每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程才可以进来执行。

                        写法:

                                synchronized(同步锁) { 访问共享资源的核心代码 }

                        同步锁的注意事项:对于当前同时执行的线程来说,同步锁必须是同一把(同一个对象),否则会出bug。

                        锁对象的使用规范:

                                建议使用共享资源作为锁对象

                                对于实例方法建议使用this作为锁对象

                                对于静态方法建议使用字节码(类名.class)对象作为锁对象

                

package com.itheima.day12.teacher.synchronize01;
// 先定义账户类
public class Account {private String cardId;private double money;//余额public Account() {}public Account(String cardId, double money) {this.cardId = cardId;this.money = money;}/*设计一个取钱的方法参数   取得钱 金额返回值  不需要*/public void drawMoney(double money){// 使用同步代码块方式加锁  保证两个线程只要一个线程在修改共享资源//  锁对象怎么选择  --- 只要保证 两个线程对象将来公用一把锁synchronized (this){ //() 里面的对象具备唯一性 "lock" 字符串一旦创建不能能改//直接写字符串  可以 但是不规范  开发规范当前共享资源  一般普通方法写 this  静态方法写 类名.class//  局部位置money 代表取的钱//  成员位置money 代表余额//  this.money -= money;// 知道是谁来取的----哪个线程执行到这里了 谁就是哪个线程String name = Thread.currentThread().getName();// 判断余额是否充足if(this.money >= money){//可以取System.out.println(name+"来取钱:"+money+" 成功!");this.money -= money;System.out.println(name+"取钱之后的余额:"+this.money);}else {System.out.println(name+"来取钱:余额不足,请充值后再去...");}}}/*** 获取* @return cardId*/public String getCardId() {return cardId;}/*** 设置* @param cardId*/public void setCardId(String cardId) {this.cardId = cardId;}/*** 获取* @return money*/public double getMoney() {return money;}/*** 设置* @param money*/public void setMoney(double money) {this.money = money;}public String toString() {return "Account{cardId = " + cardId + ", money = " + money + "}";}
}-----------------------
package com.itheima.day12.teacher.synchronize01;/*取钱线程类*/
public class DrawMoney extends Thread{// 定义一个账户的成员变量  --初始化交给了构造 由外界传递进来private Account account;//  构造第一个参数 是 取钱的账户对象  第二参数 表示线程的名字public DrawMoney(Account account, String name){super(name);this.account = account;}@Overridepublic void run() {//取钱就是在账户里面 减少余额// 调用账户对象 的 取钱方法// 小明线程 小红线程 操作的 账户是同一个// 在测试类创建 一个账户 传递进来account.drawMoney(100000); // 通过账户对象 取钱 (取的钱数)}
}---------------------
package com.itheima.day12.teacher.synchronize01;public class ThreadTest {public static void main(String[] args) {//创建一个账户对象   小明和小红 共享账户   卡号        余额Account account = new Account("ICBC-114",100000);//创建两个线程对象 分别代表小红 和 小明// 线程对象中传递 共享的账户 以及 线程名字new DrawMoney(account,"小明").start();new DrawMoney(account,"小红").start();}
}

                方式二:同步方法

                        作用:把访问共享资源的核心方法给上锁,以此保证线程安全。

                        原理:每次只能一个线程进入,执行完毕以后自动解锁,其他线程才可以进来执行。

                        写法:        

                                修饰符 synchronized 返回值类型 方法名称(形参列表) { 操作共享资源的代码 }

                        同步方法底层原理:

                                1.同步方法其实底层也是有隐式锁对象的,只是锁的范围是整个方法代码。

                                2.如果方法是实例方法:同步方法默认用this作为的锁对象。

                                3.如果方法是静态方法:同步方法默认用类名.class作为的锁对象。

                是同步代码块好还是同步方法好一点?

                        范围上:同步代码块锁的范围更小,同步方法锁的范围更大。

                        可读性:同步方法更好。

                

package com.itheima.day12.teacher.synchronize02;
// 先定义账户类
public class Account {private String cardId;private double money;//余额public Account() {}public Account(String cardId, double money) {this.cardId = cardId;this.money = money;}/*设计一个取钱的方法参数   取得钱 金额返回值  不需要同步方法就是将锁 固定到方法上了  整个方法上锁了方法声明位置 加入 synchronized 关键字*/public synchronized void drawMoney(double money){//  局部位置money 代表取的钱//  成员位置money 代表余额//  this.money -= money;// 知道是谁来取的----哪个线程执行到这里了 谁就是哪个线程String name = Thread.currentThread().getName();// 判断余额是否充足if(this.money >= money){//可以取System.out.println(name+"来取钱:"+money+" 成功!");this.money -= money;System.out.println(name+"取钱之后的余额:"+this.money);}else {System.out.println(name+"来取钱:余额不足,请充值后再去...");}}/*** 获取* @return cardId*/public String getCardId() {return cardId;}/*** 设置* @param cardId*/public void setCardId(String cardId) {this.cardId = cardId;}/*** 获取* @return money*/public double getMoney() {return money;}/*** 设置* @param money*/public void setMoney(double money) {this.money = money;}public String toString() {return "Account{cardId = " + cardId + ", money = " + money + "}";}
}-----------------
package com.itheima.day12.teacher.synchronize02;/*取钱线程类*/
public class DrawMoney extends Thread{// 定义一个账户的成员变量  --初始化交给了构造 由外界传递进来private Account account;//  构造第一个参数 是 取钱的账户对象  第二参数 表示线程的名字public DrawMoney(Account account, String name){super(name);this.account = account;}@Overridepublic void run() {//取钱就是在账户里面 减少余额// 调用账户对象 的 取钱方法// 小明线程 小红线程 操作的 账户是同一个// 在测试类创建 一个账户 传递进来account.drawMoney(100000); // 通过账户对象 取钱 (取的钱数)}
}-----------------
package com.itheima.day12.teacher.synchronize02;public class ThreadTest {public static void main(String[] args) {//创建一个账户对象   小明和小红 共享账户   卡号        余额Account account = new Account("ICBC-114",100000);//创建两个线程对象 分别代表小红 和 小明// 线程对象中传递 共享的账户 以及 线程名字new DrawMoney(account,"小明").start();new DrawMoney(account,"小红").start();}
}

                方式三:Lock锁

                        Lock锁是JDK5开始提供的一个新的锁定操作,通过它可以创建出锁对象进行加锁和解锁,更灵活、更方便、更强大。

                        Lock是接口,不能直接实例化,可以采用它的实现类ReentrantLock来构建Lock锁对象。

        

package com.itheima.day12.teacher.lock;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;// 先定义账户类
public class Account {private String cardId;private double money;//余额public Account() {}public Account(String cardId, double money) {this.cardId = cardId;this.money = money;}/*设计一个取钱的方法参数   取得钱 金额返回值  不需要Lock 都是加锁原理底层 都是每次只允许一个线程加锁 加锁之后才能访问,手动解锁 其他线程可以再加锁进来。灵活的加锁和释放锁两个方法lock() 加锁  unlock() 释放锁Lock接口 用它的实现类*///成员位置创建一个 Lock锁对象 锁对象不能被改 所以 加上final修饰private final Lock lock = new ReentrantLock();public  void drawMoney(double money){//  局部位置money 代表取的钱//  成员位置money 代表余额//  this.money -= money;// 知道是谁来取的----哪个线程执行到这里了 谁就是哪个线程String name = Thread.currentThread().getName();// 判断余额是否充足lock.lock();//加锁if(this.money >= money){//可以取System.out.println(name+"来取钱:"+money+" 成功!");this.money -= money;System.out.println(name+"取钱之后的余额:"+this.money);}else {System.out.println(name+"来取钱:余额不足,请充值后再去...");}lock.unlock();//释放锁}/*** 获取* @return cardId*/public String getCardId() {return cardId;}/*** 设置* @param cardId*/public void setCardId(String cardId) {this.cardId = cardId;}/*** 获取* @return money*/public double getMoney() {return money;}/*** 设置* @param money*/public void setMoney(double money) {this.money = money;}public String toString() {return "Account{cardId = " + cardId + ", money = " + money + "}";}
}---------------------
package com.itheima.day12.teacher.lock;/*取钱线程类*/
public class DrawMoney extends Thread{// 定义一个账户的成员变量  --初始化交给了构造 由外界传递进来private Account account;//  构造第一个参数 是 取钱的账户对象  第二参数 表示线程的名字public DrawMoney(Account account, String name){super(name);this.account = account;}@Overridepublic void run() {//取钱就是在账户里面 减少余额// 调用账户对象 的 取钱方法// 小明线程 小红线程 操作的 账户是同一个// 在测试类创建 一个账户 传递进来account.drawMoney(100000); // 通过账户对象 取钱 (取的钱数)}
}---------------------
package com.itheima.day12.teacher.lock;public class ThreadTest {public static void main(String[] args) {//创建一个账户对象   小明和小红 共享账户   卡号        余额Account account = new Account("ICBC-114",100000);//创建两个线程对象 分别代表小红 和 小明// 线程对象中传递 共享的账户 以及 线程名字new DrawMoney(account,"小明").start();new DrawMoney(account,"小红").start();}
}

  6.线程通信

        1.什么是线程通信?

                当多个线程共同操作共享的资源时,线程间通过某种方式互相告知自己的状态,以相互协调,并避免无效的资源争夺。

        2.线程通信的常见模型(生产者与消费者模型)

                  生产者线程负责生产数据

                  消费者线程负责消费生产者生产的数据。

                   注意:生产者生产完数据应该等待自己,通知消费者消费;消费者消费完数据也应该等待自己,再通知生产者生产!

        3.Object类的等待和唤醒方法:

                

        注意事项:上述方法应该使用当前同步锁对象进行调用。

package com.itheima.day12.teacher.wait_notify;import java.util.ArrayList;
import java.util.List;/*定义桌子类定义一个存储包子的集合 存储包子定义一个厨师生产包子的方法定义一个吃货吃包子的方法*/
public class Desk {//  定义一个存储包子的集合 存储包子private List<String> list = new ArrayList<>();// 定义一个厨师生产包子的方法public synchronized void put()  {try{//获取厨师线程对象的名称String name = Thread.currentThread().getName();//判断有没有包子if(list.size()==0){//没有包子 厨师要做包子list.add(name+"做的包子..");//生产包子 把包子放到集合中System.out.println("厨师:"+name+"正在做包子.....");//模拟做包子的时间Thread.sleep(2000);//等待和唤醒方法 一般采用共享资源进行调用//包子做完了this.notifyAll();//唤醒所有的吃货this.wait();//厨师进入休息 等待}else {
//                this.notifyAll();//唤醒所有的吃货this.wait();//厨师进入休息 等待 因为桌子上有包子了。}}catch (InterruptedException e){e.printStackTrace();}}//定义一个 吃货 吃包子的方法public synchronized  void get() {try{//获取吃货的名称String name = Thread.currentThread().getName();//先判断有没有包子if(list.size()==1){//模拟吃包子System.out.println("吃货:"+name+"正在吃"+list.get(0));list.clear();//清空Thread.sleep(1500);//吃饭之后//唤醒厨师this.notifyAll();//吃货休息this.wait();}else {//唤醒厨师
//                this.notifyAll();//吃货休息this.wait();}}catch (Exception e){e.printStackTrace();}}}---------------------
package com.itheima.day12.teacher.wait_notify;public class QingFengBaoZiPu {public static void main(String[] args) {//开始准备吃包子System.out.println("进入庆丰包子铺 坐了下来 点餐");/*new Thread().start() 是开启一个新的线程new Thread(线程任务,线程名字).start()给新的线程绑定一个线程任务和线程的名字new Thread(()->{},线程名字).start()因为线程任务是 函数式接口 所以可以使用 lambda去表达()->{//厨师要生产包子while(true){desk.put();}}厨师生产包子  只要包子没有了就可以去生产  所以写了一个循环()->{//吃货要次包子while(true){desk.get();}}吃货吃包子 只要包子还有 就可以吃  所以也写了一个循环*///创建共享资源Desk desk = new Desk();//有三个厨师  线程new Thread(()->{//厨师要生产包子while(true){desk.put();}},"霍大厨").start();new Thread(()->{//厨师要生产包子while(true){desk.put();}},"雷大厨").start();new Thread(()->{//厨师要生产包子while(true){desk.put();}},"卧龙凤厨").start();//有两个吃货 线程new Thread(()->{//吃货要次包子while(true){desk.get();}},"乐吃货").start();new Thread(()->{//吃货要次包子while(true){desk.get();}},"李逵吃货").start();}
}

7.线程池

        1.什么是线程池?        

                线程池就是一个可以复用线程的技术。

        2.如何创建线程池?

                方式一:使用ExecutorService的实现类ThreadPoolExecutor创建一个线程池对象。

package com.itheima.day12.teacher.threadpool;public class CuoZao implements Runnable{@Overridepublic void run() {//任务是搓澡String name = Thread.currentThread().getName();System.out.println("号码为:"+name+"的师傅正在给客人搓澡=====盐搓---奶搓---醋搓--");//模拟搓澡时间try {//5秒搓一个Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}}
}--------------
package com.itheima.day12.teacher.threadpool;import java.util.concurrent.*;public class Demo {public static void main(String[] args) throws InterruptedException {// 先构建一个 线程池对象ExecutorService pool = new ThreadPoolExecutor(3,//核心线程数量5,// 最大线程数量 = 核心线程数量+临时线程数量;8,//临时存活时间  时间的数量TimeUnit.SECONDS,// 超过核心现场后 如果有线程超过8秒中没有被使用 就销毁掉new ArrayBlockingQueue<>(4),//指定任务队列 任务阻塞队列  阻塞长度是4Executors.defaultThreadFactory(),//用户创建线程对象的工程对象 固定代码new ThreadPoolExecutor.CallerRunsPolicy());//任务拒绝策略 四个 我选取最后 忙不过来 找外援// 线程池  澡堂//  核心线程数量  老板招聘的 三个搓澡师傅//执行搓澡任务CuoZao cz = new CuoZao();//来一个顾客 搓一个顾客//接客pool.execute(cz);//核心pool.execute(cz);//核心pool.execute(cz);//核心//三个客人pool.execute(cz);//第四个客人 先等待了  核心线程为他服务pool.execute(cz);//第五个客人pool.execute(cz);//第六个客人pool.execute(cz);//第七个客人// 7-3 = 4pool.execute(cz);//第八个客人 触发了 招聘临时工  阻塞队列4 一旦超出阻塞队列 就增派人手pool.execute(cz);//第九个客人                  阻塞队列4  超出阻塞队列两个 增派两个人手// 第九个客人 已经有五个搓澡师傅 已经达到 最大线程数量pool.execute(cz);//第十个客人   阻塞队列满了 超出阻塞队列的 用两个人手  但是还少一个// 这个时候拒绝策略 -- 增派人手  main来处理。。。Thread.sleep(20000);//时间过了十一秒 没有新的任务 肯定有线程没有处理任务的 这种任务就会销毁System.out.println("至少空闲了12秒 已经有被销毁的线程了...销毁之后 ");pool.execute(cz);pool.execute(cz);pool.execute(cz);pool.execute(cz);pool.execute(cz);pool.shutdown();//都搓完了 把 澡堂关闭
//        pool.shutdownNow();//里面关闭 没搓完的任务回到队列中// 临时线程什么时候创建?  核心线程忙 + 任务队列满了。可以创建临时线程。// 如果临时线程也满了,触发了拒绝策略,//  可能1: 找主线程帮忙处理。//   可能2: 抛弃 说声对不起  异常//    可能3: 抛弃 什么也不说//    可能4: 抛弃对头  放入新的}
}

submit方法:

        

package com.itheima.day12.teacher.threadpool;import java.util.concurrent.Callable;public class Download implements Callable<String> {@Overridepublic String call() throws Exception {System.out.println(Thread.currentThread().getName()+"正在下载程序.....");return "任务下载完成";}
}--------------------
package com.itheima.day12.teacher.threadpool;import java.util.concurrent.*;public class XunLei {public static void main(String[] args) throws ExecutionException, InterruptedException {ExecutorService pool = new ThreadPoolExecutor(3,//核心线程数量5,// 最大线程数量 = 核心线程数量+临时线程数量;8,//临时存活时间  时间的数量TimeUnit.SECONDS,// 超过核心现场后 如果有线程超过8秒中没有被使用 就销毁掉new ArrayBlockingQueue<>(4),//指定任务队列 任务阻塞队列  阻塞长度是4Executors.defaultThreadFactory(),//用户创建线程对象的工程对象 固定代码new ThreadPoolExecutor.CallerRunsPolicy());//任务拒绝策略 四个 我选取最//线程池 处理 带返回值的任务Download d = new Download();//下载任务Future<String> f1 = pool.submit(d);pool.submit(d);pool.submit(d);pool.submit(d);pool.submit(d);System.out.println(f1.get());}
}

                方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象。

                线程池的注意事项: 

                        1、临时线程什么时候创建?

                                新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。

                        2、什么时候会开始拒绝新任务?

                                核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始拒绝任务。

       3.使用ExecutorService线程池对象的常用方法

                        void execute(Runnable command)

        4.新任务拒绝策略

        

        5.线程池处理Callable任务

        使用ExecutorService线程池对象的常用方法---Future<T> submit(Callable<T> task)

        6.Executors工具类实现线程池

                是一个线程池的工具类,提供了很多静态方法用于返回不同特点的线程池对象。

        注意 :这些方法的底层,都是通过线程池的实现类ThreadPoolExecutor创建的线程池对象。

        7.Executors使用可能存在的陷阱

                大型并发系统环境中使用Executors如果不注意可能会出现系统风险。

package com.itheima.day12.teacher.threadpool;import java.util.concurrent.Callable;public class Download implements Callable<String> {@Overridepublic String call() throws Exception {System.out.println(Thread.currentThread().getName()+"正在下载程序.....");return "任务下载完成";}
}-------------------
package com.itheima.day12.teacher.threadpool;import java.util.concurrent.*;public class XunLei2 {public static void main(String[] args) throws ExecutionException, InterruptedException {ExecutorService pool = Executors.newFixedThreadPool(3);//线程池 处理 带返回值的任务Download d = new Download();//下载任务Future<String> f1 = pool.submit(d);pool.submit(d);pool.submit(d);pool.submit(d);pool.submit(d);System.out.println(f1.get());}
}

8.其它细节知识:并发、并行

        1.进程:

                正在运行的程序(软件)就是一个独立的进程。

                线程是属于进程的,一个进程中可以同时运行很多个线程。

                进程中的多个线程其实是并发和并行执行的。

        2.并发的含义

                进程中的线程是由CPU负责调度执行的,但CPU能同时处理线程的数量有限,为了保证全部线程都能往前执行,CPU会轮询为系统的每个线程服务,由于CPU切换的速度很快,给我们的感觉这些线程在同时执行,这就是并发。

        3.并行的理解

                在同一个时刻上,同时有多个线程在被CPU调度执行。

        4.多线程是怎么执行的?

                并发和并行同时进行的

9.其它细节知识:线程的生命周期   

        1.线程的6种状态           

        

       2.线程6中状态互相转换  

          

        

                             

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Day04:APP架构小程序H5+Vue语言Web封装原生开发Flutter

目录 常见APP开发架构 APP-开发架构-原生态-IDEA APP-开发架构-Web封装-平台 APP-开发架构-H5&Vue-HBuilderX WX小程序-开发架构-Web封装-平台 WX小程序-开发架构-H5&Vue-HBuilderX 思维导图 章节知识点&#xff1a; 应用架构&#xff1a;Web/APP/云应用/三方服…

ARM系列 -- 虚拟化(四)

今天来看看虚拟中断。 在一个非虚拟化的系统中&#xff0c;操作系统可以直接访问GIC的寄存器&#xff0c;并且处理GIC的物理中断接口&#xff08;physical interrupt interface&#xff09;。 但是在一个虚拟化的系统中&#xff0c;不是这样。Guest OS并不知道它运行在虚拟系…

51单片机晶振频率与定时中断产生pwn占空比

单片机中晶振频率为12MHZ的机器周期怎么算? 1、系统晶振频率是12M&#xff0c;则机器周期&#xff1d;12&#xff0f;12&#xff1d;1us&#xff1b; 2、定时1ms&#xff1d;1&#xff0a;1000&#xff1d;1000us&#xff1b; 3、工作在方式0下&#xff1a;最大计数值是2&a…

Linux的gdb调试

文章目录 一、编译有调试信息的目标文件二、启动gdb调试文件1、查看内容list/l&#xff1a;l 文件名:行号/函数名&#xff0c;l 行号/函数名2、打断点b&#xff1a;b文件名:行号/函数名&#xff0c;b 行号/函数名 与 查看断点info/i&#xff1a;info b3、删除断点d&#xff1a;…

unity shaderGraph实例-物体线框显示

文章目录 本项目基于URP实现一&#xff0c;读取UV网格&#xff0c;由自定义shader实现效果优缺点效果展示模型准备整体结构各区域内容区域1区域2区域3区域4shader属性颜色属性材质属性后处理 实现二&#xff0c;直接使用纹理&#xff0c;使用默认shader实现优缺点贴图准备材质准…

在CentOS上使用Docker搭建Halo博客并实现远程访问的详细指南

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;网络奇遇记、数据结构 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. Docker部署Halo1.1 检查Docker版本1.2 在Docker中部署Halo 二. Linux安装Cpol…

MATLAB中的makeweight函数

W makeweight(dcgain,[freq,mag],hfgain) W makeweight(dcgain,[freq,mag],hfgain,Ts) W makeweight(dcgain,[freq,mag],hfgain,Ts,N) W makeweight(dcgain,wc,hfgain,___) W makeweight(dcgain,wc,hfgain&#xff0c;___)表示增益交叉频率wc。该语法相当于将…

JavaSec 基础之 Maven

文章目录 Maven介绍作用Maven 下载与安装IDEA 集成 Maven配置Maven环境创建 Maven 项目导入 maven 项目MAVEN管理jar包 Maven 介绍 Maven是一个跨平台的项目管理工具。作为Apache组织的一个颇为成功的开源项目&#xff0c;其主要服务于基于Java平台的项目创建&#xff0c;依赖…

Keil新版本安装编译器ARMCompiler 5.06

0x00 缘起 我手头的项目在使用最新版本的编译器后&#xff0c;烧录后无法正常运行&#xff0c;故安装5.06&#xff0c;测试后发现程序运行正常&#xff0c;以下为编译器的安装步骤。 0x01 解决方法 1. 下载编译器安装文件&#xff0c;可以去ARM官网下载&#xff0c;也可以使用我…

windows系统使用Vscode在WSL调试golang本地进程

背景&#xff1a; windows10企业版 vscodegolang1.20 wsl编译运行。 vscode 使用本地wsl进行进程attach操作&#xff0c;发现&#xff1a;Access is denied. 本地进程启动&#xff0c;vscode调试进程。windows-Linux控制台: Starting: C:\Users\book\go\bin\dlv.exe dap --l…

【kubernetes】关于k8s集群的声明式管理资源

目录 一、声明式管理方法 二、资源配置清单管理 1、导出资源配置清单 2、修改资源配置清单并应用 2.1离线修改 2.2在线修改 三、通过资源配置清单创建资源对象 获取K8S资源配置清单文件模板&#xff1f; 关于配置清单常见的字段 方案一&#xff1a;手写yaml配置文件 …

水库安全监测方案(福建地区水库安全监测案例分享)

我司星创易联最近在福建省受到了一个水库安全监测系统项目的委托。该水库位于福建中部山区,作为该地区的重要防洪与供水工程,对下游数十万人的生活产生重大影响。但是因为水库附近地质情况复杂,水库大坝在多次洪水冲击下出现一定病害,亟须全面加强对水库大坝安全状况的监测,以确…

ESP32语音转文字齐护百度在线语音识别

一、导入(10分钟&#xff09; 学习目的 二、新授(70分钟) 1.预展示结果(5分钟) 2.本节课所用的软硬件(5分钟) 4.图形化块介绍(10分钟) 5.单个模块的简单使用(10分钟) 6.在线语音转换工具逻辑分析(10分钟) 7.在线语音转换工具分步实现(30分钟) 三、巩固练习(5分钟) 四、课堂小结…

【QT+QGIS跨平台编译】之五十四:【QGIS_CORE跨平台编译】—【qgssqlstatementlexer.cpp生成】

文章目录 一、Flex二、生成来源三、构建过程一、Flex Flex (fast lexical analyser generator) 是 Lex 的另一个替代品。它经常和自由软件 Bison 语法分析器生成器 一起使用。Flex 最初由 Vern Paxson 于 1987 年用 C 语言写成。 “flex 是一个生成扫描器的工具,能够识别文本中…

H3C OSPF 外部路由引入实验

H3C OSPF 外部路由引入实验 实验拓扑 实验需求 按照图示配置 IP 地址R1&#xff0c;R2&#xff0c;R3 运行 OSPF 使内网互通&#xff0c;所有接口&#xff08;公网接口除外&#xff09;全部宣告进 Area 0&#xff1b;要求使用环回口作为 Router-id业务网段不允许出现协议报文…

Groovy(第九节) Groovy 之单元测试

JUnit 利用 Java 对 Song 类进行单元测试 默认情况下 Groovy 编译的类属性是私有的,所以不能直接在 Java 中访问它们,必须像下面这样使用 setter: 编写这个测试用例余下的代码就是小菜一碟了。测试用例很好地演示了这样一点:用 Groovy 所做的一切都可以轻易地在 Java 程序…

科技论文编写思路

科技论文编写思路 1.基本框架2.课题可行性评估1.研究目标和意义2.研究方法和技术3.可行性和可操作性4.风险和不确定性5.经济性和资源投入6.成果预期和评估 3.写作思路4.利用AI读论文5.实验流程 1.基本框架 IntroductionRelated worksMethodExperiment and analysisDiscussionC…

计算机网络:深入探索HTTP

引言&#xff1a; HTTP&#xff0c;全称超文本传输协议&#xff08;Hypertext Transfer Protocol&#xff09;&#xff0c;是互联网上数据通信的基础。它定义了客户端&#xff08;如浏览器&#xff09;和服务器之间如何交互和传输数据。HTTP最初是为了支持Web浏览而设计的&…

【Web安全靶场】sqli-labs-master 1-20 BASIC-Injection

sqli-labs-master 1-20 BASIC-Injection 文章目录 sqli-labs-master 1-20 BASIC-Injection第一关-报错注入第二关-报错注入第三关-报错注入第四关-报错注入第五关-报错注入-双查询注入第六关-报错注入-双查询注入第七关-outfile写入webshell第八关-布尔盲注第九关-时间盲注第十…

小甲鱼Python06 序列字典集合

一、序列 1.id函数 is运算符 我们首先思考下字符串、元组、列表的共同点&#xff1a; 都有很多共同的运算符。都可以通过索引来获取元素&#xff0c;第一个元素索引都是0&#xff0c;都可以通过切片的方法获取某个范围内元素的集合。 以上三种统称为序列。序列分为可变序列…