第四章Java核心类库_多线程
第四章第五节Java核心类库_多线程
- 多线程
- 一.线程与进程
- 1.线程与进程
- 2.线程调度
- 二.同步与异步&并发与并行
- 1. 同步与异步
- 2. 并发与并行
- 三.继承Thread
- 1.代码块
- 2.运行结果
- 3.时序图
- 四.实现Runnable(推荐使用)
- 1.使用方法
- 2.代码块
- 3.thread也有一定的好处
- 五.Thread类
- 1.常用构造方法
- 2.其他常用方法
- 六.设置和获取线程名称
- 七.线程休眠sleep
- 八.线程的中断
- 1.代码
- 2.运行结果
- 九.守护线程
- 十.线程安全
- 解决方案1-同步代码块
- 解决方案2-同步方法
- 解决方案3-显式锁Lock
- 十一.公平锁与非公平锁
- 十二.死锁典例
- 十三.多线程通信问题
- 十四.生产者与消费者
- Test one:
- Test two:
- Test three:
- 十五.线程的六种状态
- 十六.线程创建的第三种方式-带返回值的线程Callable
- 十七.线程池 Executors
- 十八.Lambda表达式
多线程
一.线程与进程
1.线程与进程
程序(program):
是为了完成特定任务、用某种语言编写的一组指令的集合,是一段静态的代码。(程序是静态的)
进程(process):
是程序的一次执行过程。正在运行的一个程序,进程作为资源分配的单位,在内存中会为每个进程分配不同的内存区域。
进程是动态的,是一个动的过程,有它自身的产生、存在和消亡的过程。
线程(thread):
进程可以进一步细化为线程,是一个程序内部的一条执行路径。若一个进程同一时间并行执行多个线程,就是支持多线程。
2.线程调度
目的是为了更合理的利用CPU
分时调度:
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
抢占式调度(Java使用的调度方式):
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核新而言,某个时刻,只能执行一个线程,而CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻运行。
但其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。
二.同步与异步&并发与并行
1. 同步与异步
同步: 排队执行 ,效率低但是安全
异步: 同时执行 ,效率高但是数据不安全
2. 并发与并行
并发:指两个或多个事件在同一个时间段内发生。
并行:指两个或多个事件在同一时刻发生(同时发生)。
三.继承Thread
1.代码块
Demo1
public class Demo1 {public static void main(String[] args) {MyThread myThread = new MyThread();myThread.start();for(int i = 0; i < 10; i++){System.out.println("我要睡觉!"+i);}}
MyThread
public class MyThread extends Thread{/*** run方法就是线程要执行的任务方法*/@Overridepublic void run() {//这里的代码就是一条新的执行路径//这个执行路径的触发方式不是调用run方法,//而是通过thread对象的start()来启动任务for(int i = 0; i < 10; i++){System.out.println("我要吃饭!"+i);}}
}
2.运行结果
3.时序图
补充:每个线程都有自己的栈空间,共用一份堆内存。
四.实现Runnable(推荐使用)
1.使用方法
另一种实现多线程的方法之步骤:
①创建自定义类implements实现Runnable接口,并重写run方法;
②用自定义类创建一个对象r;
③用Thread类创建一个对象t,并将r作为t构造方法的参数;
2.代码块
实现Runnable与继承Thread相比有如下优势
1,通过创建任务,然后给线程分配任务的方式实现多线程,更适合多个线程同时执行任务的情况;
2,可以避免单继承所带来的局限性(Java允许实现多个接口,但不允许继承多个父类);
3,任务与线程是分离的,提高了程序的健壮性;
4,后期学习的线程池技术,接受Runnable类型的任务,不接受Thread类型的线程;
Demo1
public class Demo1 {public static void main(String[] args) {//实现Runnable//1. 创建一个任务对象MyRunnable r = new MyRunnable();//2. 创建一个线程,并为其分配一个任务Thread t = new Thread(r);//3. 执行这个线程t.start();for(int i = 0; i < 10; i++){System.out.println("我要睡觉!"+i);}}
}
MyRunnable
/*** 用于给线程执行的任务*/
public class MyRunnable implements Runnable {@Overridepublic void run() {//线程的任务for (int i=0;i<10;i++){System.out.println("我要上厕所"+i);}}
}
3.thread也有一定的好处
public class Demo1 {public static void main(String[] args) {//若只调用一次,用thread可以通过匿名内部类的方式,使代码更加简洁new Thread(){@Overridepublic void run() {for (int i=0;i<10;i++){System.out.println("我要睡觉"+i);}}}.start();for (int i=0;i<10;i++){System.out.println("我要上厕所"+i);}}
}
五.Thread类
1.常用构造方法
2.其他常用方法
用于设置优先级的字段
六.设置和获取线程名称
MyRunnable
/*** 用于给线程执行的任务*/
public class MyRunnable implements Runnable {@Overridepublic void run() {//线程的任务//静态方法currentThread可以获得当前线程,调用getName/setName// 可以获得/设置线程的名称System.out.println(Thread.currentThread().getName());}
}
Demo1
public class Demo1 {public static void main(String[] args) {System.out.println(Thread.currentThread().getName());new Thread(new MyRunnable(),"喜羊羊").start();new Thread(new MyRunnable(),"美羊羊").start();new Thread(new MyRunnable(),"懒羊羊").start();}
}
运行结果
七.线程休眠sleep
public class Demo1 {public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 10; i++){System.out.println(i);Thread.sleep(1000);}}
}
八.线程的中断
过时的stop方法可以直接中断线程,但是如果线程来不及释放资源,会造成一部分垃圾无法回收;
这里采用添加中断标记的方法:调用interrupt方法,子线程执行时捕获中断异常,并在catch块中,添加处理释放资源的代码。
1.代码
public class Demo1 {public static void main(String[] args) throws InterruptedException {//线程的中断//一个线程是一个独立的执行路径,它是否应该结束,应该由其自身决定Thread t = new Thread(new MyRunnable(), "子线程");t.start();for (int i =0;i < 5;i++){System.out.println(Thread.currentThread().getName()+":"+i);Thread.sleep(1000);}t.interrupt();//主线程for循环执行完毕后,将子线程中断}static class MyRunnable implements Runnable {@Override//这里的run方法不能将异常抛出,因为这里父接口Runnable没有声明异常的抛出//子接口不能声明比父接口范围更大的异常//所以只能进行try-catchpublic void run() {for (int i =1;i <= 10;i++){System.out.println(Thread.currentThread().getName()+":"+i);try {Thread.sleep(1000);} catch (InterruptedException e) {System.out.println("我挂机了嘤嘤嘤~");return;}}}}}
2.运行结果
九.守护线程
概述:
线程分为守护线程和用户线程;
- 用户线程:当一个进程不包含任何存活的用户线程时,进程结束;
- 守护线程:守护用户线程,当最后一个用户线程结束后,所有守护线程自动死亡;
直接创建的都是用户线程;
设置守护线程:线程对象.setDaemon(true),而且一定要在启动之前设置。
十.线程安全
线程不安全的原因:
多个线程争抢同一个数据,使得数据在判断和使用时出现不一致的情况。
解决方案1-同步代码块
public class Demo1 {public static void main(String[] args) throws InterruptedException {Object o = new Object();//线程不安全//解决方案1 同步代码块//格式:synchronized(锁对象){}Runnable run = new Ticket();new Thread(run).start();new Thread(run).start();new Thread(run).start();}static class Ticket implements Runnable{//总票数private int count = 10;private Object o = new Object();@Overridepublic void run() {//Object o = new Object(); //这里不是同一把锁,所以锁不住while (true) {synchronized (o) {if (count > 0) {//卖票System.out.println("正在准备卖票");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}count--;System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);}else {break;}}//synchronized结束}//while结束}//run结束}//Ticket结束
}//Demo1结束
解决方案2-同步方法
public class Demo1 {public static void main(String[] args) throws InterruptedException {Object o = new Object();//线程不安全//解决方案2 同步方法Runnable run = new Ticket();new Thread(run).start();new Thread(run).start();new Thread(run).start();}static class Ticket implements Runnable{//总票数private int count = 10;@Overridepublic void run() {while (true) {boolean flag = sale();if(!flag){break;}}}public synchronized boolean sale(){if (count > 0) {//卖票System.out.println("正在准备卖票");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}count--;System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);return true;}return false;}}
}
给方法上锁,对应的锁对象就是this, 如果是静态修饰方法的话,锁对象为类名.class;
比如这里sale方法若被修饰为静态方法的话,锁对象为Ticket.class,也就是字节码文件对象。
解决方案3-显式锁Lock
同步方法和同步代码块都属于隐式锁,显式锁则是程序员手动加锁、解锁;
public class Demo{public static void main(String[] args) {Object o = new Object();//线程不安全//解决方案3 显示锁 Lock 子类 ReentrantLockRunnable run = new Ticket();new Thread(run).start();new Thread(run).start();new Thread(run).start();}static class Ticket implements Runnable{//总票数private int count = 10;//参数为true表示公平锁 默认是false 不是公平锁private Lock l = new ReentrantLock(true);@Overridepublic void run() {while (true) {//锁起来!l.lock();if (count > 0) {//卖票System.out.println("正在准备卖票");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}count--;System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);}else {break;}//把锁打开!l.unlock();}}}
}
十一.公平锁与非公平锁
-区别
公平锁:先来先得,遵循排队;
非公平锁:大家一起抢(同步代码块,同步方法,显式锁都属于非公平锁);
-实现方法
在显式锁实例化时,传入参数true()
//参数为true表示公平锁 默认是false 不是公平锁
private Lock l = new ReentrantLock(true);
十二.死锁典例
public class Demo {/*** 死锁典例:两个线程互相申请对方的锁,但是对方都不释放锁。* @param args*/public static void main(String[] args) {//线程死锁Culprit culprit = new Culprit();Police police = new Police();//新建一个线程,警察对罪犯说话,等待罪犯回应//警察的say方法里面打印完自己说的话之后,便等待罪犯的回应//而另一边罪犯的say方法也在等待警察的回应,且罪犯的say是带锁的//故罪犯的reaction方法一直到不了,警察的say就一直在等罪犯的reactionnew MyThread(culprit,police).start();//main主线程,罪犯对警察说话,等待警察回应//罪犯的say方法里面打印完自己说的话之后,便等待警察的回应//而另一边警察的say方法也在等待罪犯的回应,且警察的say是带锁的//故警察的reaction方法一直到不了,罪犯的say就一直在等警察的reactionculprit.say(police);}static class MyThread extends Thread{//私有两个属性,一个警察p一个罪犯cprivate Culprit c;private Police p;//两参的构造方法public MyThread(Culprit c, Police p) {this.c = c;this.p = p;}@Overridepublic void run() {p.say(c);//警察对罪犯说话,等待罪犯回应}}static class Culprit{public synchronized void say(Police p){System.out.println("罪犯说:你放了我,我放了人质");p.reaction();}public synchronized void reaction(){System.out.println("罪犯说:好的,我放了人质,你放过我");}}static class Police{public synchronized void say(Culprit c){System.out.println("警察说:你放了人质,我放过你");c.reaction();}public synchronized void reaction(){System.out.println("警察说:好的,我放了你,你把人质给我");}}}
解决方案:
在任何有可能导致锁产生的方法里,不要再调用另外一个方法让另外一个锁产生;
一个方法已经产生一个锁了,就不要再去找其他有可能产生锁的方法了。
十三.多线程通信问题
主要借助于wait和notify函数实现
十四.生产者与消费者
思路:
厨师cook为生产者线程,服务员waiter为消费者线程,食物为生产与消费的物品;
假设目前只有一个厨师,一个服务员,一个盘子。理想状态是:厨师生产一份饭菜,服务员端走一份,且饭菜的属性未发生错乱;
厨师可以制作两种口味的饭菜,制作100次;
服务员可以端走饭菜100次。
Test one:
public class TestOne{public static void main(String[] args) {//多线程通信 生产者与消费者问题Food f = new Food();new Cook(f).start();new Waiter(f).start();}//厨师static class Cook extends Thread{private Food f;//构造方法public Cook(Food f) {this.f = f;}//做五十份老干妈小米粥和五十份煎饼果子@Overridepublic void run() {for (int i = 0; i < 100; i++) {if(i%2==0){// 设计两种菜色f.setNameAndTaste("老干妈小米粥","香辣味");}else {f.setNameAndTaste("煎饼果子","甜辣味");}//else结束}//for结束}//run结束}//Cook结束//服务员static class Waiter extends Thread{private Food f;//构造方法public Waiter(Food f) {this.f = f;}//上100次菜@Overridepublic void run() {for (int i = 0; i < 100; i++) {try {Thread.sleep(100);//端完一次休息一下:因为厨师在setNameAndTaste中有休眠100毫秒} catch (InterruptedException e) {e.printStackTrace();}f.get();//端菜}//for结束}//run结束}//Waiter结束//食物static class Food{private String name;private String taste;// 生产public void setNameAndTaste(String name,String taste){this.name = name;//设置名称和味道之间加入休眠,为了演示:在此期间时间片可能发生丢失,因而菜色属性发生错乱的情况。try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}this.taste = taste;}// 消费public void get(){System.out.println("服务员端走的菜的名称是:"+name+",味道是:"+taste);}//get结束}//Food结束}//TestOne结束
运行结果
原因分析:在厨师进行setNameAndTaste的时候,先设置了name,此时休眠了0.1秒,而taste还未来得及切换,服务员此时就把菜端出去了,出现了多线程协同合作的不匹配问题。
Test two:
给get、setNameAndTaste两个方法都加上同步标记synchronized,让厨师在进行setNameAndTaste时候服务员别进来瞎掺和进行get,服务员get的时候厨师别进来瞎掺和进行setNameAndTaste。
错误原因分析:synchronized只是确保了方法内部不会发生线程切换,但并不能保证生产一个消费一个的逻辑关系。
Test three:
终极解决方案:
厨师做完饭后喊醒服务员,自己睡着;服务员送完饭后喊醒厨师,自己睡着。主要修改的部分为setNameAndTaste与get方法。
public synchronized void setNameAndTaste(String name,String taste) {if (flag) {this.name = name;//设置名称和味道之间加入休眠,为了演示:在此期间时间片可能发生丢失,因而菜色属性发生错乱的情况。try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}this.taste = taste;//做完了把标记改为falseflag = false;//唤醒在当前this对象下所有睡着的线程(即唤醒服务员)this.notifyAll();//厨师自己睡过去try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}
}//setNameAndTaste结束// 消费
public synchronized void get(){if (!flag){System.out.println("服务员端走的菜的名称是:"+name+",味道是:"+taste);//端完了把标记改为trueflag = true;//唤醒在当前this对象下所有睡着的线程(即唤醒厨师)this.notifyAll();//服务员自己睡过去try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}
}//get结束
运行结果
十五.线程的六种状态
十六.线程创建的第三种方式-带返回值的线程Callable
新的创建线程的方式。之前的创建线程方式:实现Thread的子类、实现Runnable接口,可以看成是和主线程并发执行的;这里要讲的线程更像是主线程指派的一个任务,主线程可以获得其返回值。
后续开发使用较少,仅作了解,不再赘述。
十七.线程池 Executors
为什么需要线程池?
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程 就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。
线程池就是一个容纳多个线程的容器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。
后续开发中使用到线程池的地方很少,不是重点,不再赘述,详情看老师发的pdf。
十八.Lambda表达式
为什么要用lambda表达式?
答: 对于某些应用场景,我们更注重于结果,如果能用一个方法解决, 那么通过创建对象、调用方法的方式可能会更加繁琐。
1)不使用lambda
public class Demo1 {/*** lambda表达式* 函数式编程思想(注重结果,而面向对象则是通过创建对象,解决问题)* @param args*/public static void main(String[] args) {print(new MyMath() {@Overridepublic int sum(int x, int y) {return x + y;}}, 100, 200);}public static void print(MyMath m, int x, int y){int num = m.sum(x, y);System.out.println(num);}static interface MyMath{int sum(int x, int y);}
}
2)使用lambda
不需要实现接口、实例化对象;
public class Demo1 {/*** lambda表达式* 函数式编程思想(注重结果,而面向对象则是通过创建对象,解决问题)* @param args*/public static void main(String[] args) {print((int x, int y) -> {return x + y;}, 100, 200);}public static void print(MyMath m, int x, int y){int num = m.sum(x, y);System.out.println(num);}static interface MyMath{int sum(int x, int y);}
}
参考链接:https://blog.csdn.net/qq_41528502/article/details/108052873
第四章Java核心类库_多线程相关推荐
- jre包括jvm和java核心类库_包含JVM标准实现及Java核心类库
包含JVM标准实现及Java核心类库 点击次数:1533 更新日期:2013-03-24 "青花瓷Java版"为北京师范大学教育学部蔡苏作词原创,覆盖教育技术学院专业选修课< ...
- Java核心类库篇7——多线程
Java核心类库篇7--多线程 1.程序.进程和线程 程序 - 数据结构 + 算法,主要指存放在硬盘上的可执行文件 进程 - 主要指运行在内存中的可执行文件 线程就是进程内部的程序流 操作系统内部支持 ...
- Java核心类库(下)
文章目录 Java核心类库(下) 异常机制(重点) 基本概念 异常的分类 异常的避免 异常的捕获 异常的抛出 自定义异常 异常机制总结 File类(重点) 基本概念 常用的方法 IO流 IO流的概念 ...
- 【JVM】第四章 Java内存模型
第四章 Java内存模型 文章目录 第四章 Java内存模型 一.物理机的并发问题 1.硬件的效率问题 2.缓存一致性问题 3.代码乱序执行优化问题 二.Java 内存模型 1.概念 2.Java 内 ...
- Java核心知识点学习----多线程中的阻塞队列,ArrayBlockingQueue介绍
1.什么是阻塞队列? 所谓队列,遵循的是先进先出原则(FIFO),阻塞队列,即是数据共享时,A在写数据时,B想读同一数据,那么就将发生阻塞了. 看一下线程的四种状态,首先是新创建一个线程,然后,通过s ...
- JAVA学习笔记—JAVA SE(四)JAVA核心库类(下)
文章目录 四.JAVA核心库类(下) 1. 异常机制和File类 1.1 异常机制 1.1.1 基本概念 1.1.2 异常的分类 1.1.3 异常的避免 1.1.4 异常的捕获 1.1.5 异常的抛出 ...
- Java 核心类库一览
作者:白色蜗牛 来源:蜗牛互联网 阅读本文你将收获: 类库与 JAR 文件 什么是类库 我们知道,在面向对象的程序设计里,一个类是可以调用另外一个类的方法,只要把被调用的那个类引入到 classpat ...
- 第十四章 Linux核心资源
Table of Contents, Show Frames, No Frames 第十四章 Linux核心资源 本章主要描叙寻找某个特殊核心函数时用到的Linux核心资源. 本书并不要求读者具有C编 ...
- Java核心类库篇8——网络编程
Java核心类库篇8--网络编程 1.七层网络模型 OSI(Open System Interconnect),即开放式系统互联,是ISO(国际标准化组织)组织在1985 年研究的网络互连模型. 当发 ...
最新文章
- python笔记2(函数 面向对象 文件编程 上下文管理器)
- EdgeGallery — AIO 离线部署 v1.5 版本
- mysql用的cap中哪两个_分布式事务CAP定理和BASE理论
- 混合样本数据增强(Mixed Sample Data Augmentation,MSDA)
- 控制服务器系统设计,基于 DNS 技术的顶管机远程控制系统设计与实现
- linq where的应用
- EasyUI基础入门之Parser(解析器)
- 用CSS3制作50个超棒动画效果教程
- Python实现爬取下载百度图片
- 关于TikTok环境伪装度检测,whoer和上网大师app的对比
- 同一局域网建立ftp服务器实现文件共享
- 万维网联盟W3C发布HTML5新logo
- android 动态修改logo,关于app动态修改logo的问题
- HTTP gzip压缩
- 网络代理【1】什么是网络代理
- pscc改变图片字体大小
- java 通过 冰蓝 word 转pdf ,最大程度包装pdf 样式和word接近
- Linux Socket 两个客户端通信,服务端作为中转
- java 设置背景色_背景颜色的设置
- 单片机入门学习笔记6:新唐单片机N76E003
热门文章
- 2022年全国职业技能大赛网络安全竞赛试题B模块自己解析思路(7)
- ffi Error:Dynamic Linking Error:Win32 error 126
- 一带一路中国出口马来西亚主要商品及货源地
- win7计算机广告更改,win7系统屏蔽营销广告的设置办法
- sql语句中where 1=1的作用
- java基于SpringBoot+vue 的简历模板分享系统 elementui前后端分离
- 物联网下的RFID门禁,图书防盗新变革
- angular中安装ng-alain 插件
- 【贼好理解!!】C++ list链表常用成员函数讲解
- STM32 CAN过滤器详解