电影网站网页设计,长沙网站建立公司,python网站和js做网站,武冈网络推广目录
1.多线程概述
2.多线程的创建
3.Thread的常用方法
4.线程安全
5.线程同步
6.线程通信
7.线程池
8.其它细节知识#xff1a;并发、并行
9.其它细节知识#xff1a;线程的生命周期 1.多线程概述 线程是什么#xff1f; 线程(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 CallableInteger {// 构造 方法 都可以访问 成员变量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 CallableString {//任务 求一个数的 绝对值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 是一个带有返回值的 任务--需要先交给任务管理对象 因为执行之后该线程是有返回值的 得有对象去处理返回值FutureTaskInteger task new FutureTask(my);// 在把 task对象 绑定到线程对象中Thread thread new Thread(task);// 调用start方法thread.start();// 返回值在 Task身上System.out.println(任务执行结果:task.get());MyCallable1 callable1 new MyCallable1();FutureTaskString stringFutureTask new FutureTaskString(callable1);Thread thread1 new Thread(stringFutureTask);thread1.start();String s stringFutureTask.get();System.out.println(s);//这样写没意义不能传参数只能自己定义吗
/* FutureTaskInteger integerFutureTask new FutureTask(new CallableInteger() {int a10;int b0;Overridepublic Integer call() throws Exception {return ab;}});new Thread(integerFutureTask).start(); //不执行线程是拿不到返回会值结果的
// new Thread(new FutureTaskInteger(()- 100 )).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 ListString 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 CallableString {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();//下载任务FutureString 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线程池对象的常用方法---FutureT submit(CallableT task) 6.Executors工具类实现线程池 是一个线程池的工具类提供了很多静态方法用于返回不同特点的线程池对象。 注意 这些方法的底层都是通过线程池的实现类ThreadPoolExecutor创建的线程池对象。 7.Executors使用可能存在的陷阱 大型并发系统环境中使用Executors如果不注意可能会出现系统风险。
package com.itheima.day12.teacher.threadpool;import java.util.concurrent.Callable;public class Download implements CallableString {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();//下载任务FutureString 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中状态互相转换