Java Day13 多线程

多线程

  • 1、 方式一 Thread
  • 2、实现Runnable接口
  • 3、实现 Callable接口
  • 4、与线程有关的操作方法
  • 5、线程安全问题
    • 5.1 取钱案例
    • 5.2 线程同步
      • 5.2.1 同步代码块
      • 5.2.2 同步方法
      • 5.2.3 Lock锁
  • 6、线程池
    • 6.2 创建线程池
      • 6.2.1 使用ExecutorService创建
      • 新任务策略
      • 6.2.2 使用Executors工具类创建
      • 核心线程数量问题
  • 7、并发、并行,线程生命周期
  • 7、乐观锁、悲观锁

1、 方式一 Thread

继承 Thread类
优点:编码简单
缺点:java是单继承,继承了一个类,就不能继承其他,可拓展性不强。

注意:
①子线程一定要调用start方法,而不是run方法,调用run方法,还是相当于在main线程中创建了一个实例对象,在运行她的方法, 而已,是单线程的。调用strat方法,虽然底层还是调用run方法,但是他会告诉cpu说我们另外启动了一个线程。
②子线程启动一定要在主线程任务之前。
③每次执行的结果会不同。

package com.cky.file;public class MyThread extends  Thread{@Overridepublic void run() {for (int i = 0; i <5 ; i++) {System.out.println("子线程"+i);}}
}
package com.cky.file;public class Endecode {public static void main(String[] args) throws Exception {Thread thread=new MyThread();thread.start();for (int i = 0; i <5 ; i++) {System.out.println("主线程"+i);}}}

2、实现Runnable接口

在这里插入图片描述

package com.cky.file;public class MyRunnable implements  Runnable{@Overridepublic void run() {for (int i = 0; i <5 ; i++) {System.out.println("子线程"+i);}}
}
package com.cky.file;public class Endecode {public static void main(String[] args) throws Exception {//创建一个任务对象MyRunnable myRunnable=new MyRunnable();//调用线程类的start方法new Thread(myRunnable).start();//匿名内部类Runnable runnable = new Runnable() {@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println("子线程" + i);}}};new Thread(runnable).start();//简化形式1new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println("子线程" + i);}}}).start();//形式2new Thread(()-> {for (int i = 0; i < 5; i++) {System.out.println("子线程" + i);}}).start();for (int i = 0; i <5 ; i++) {System.out.println("主线程"+i);}}}

由于Runnable是一个函数式接口,匿名内部类可以使用lambda形式
在这里插入图片描述

3、实现 Callable接口

上边两种实现方法,都不能获得线程执行的结果并返回,方法3可以。
实现Callable接口 将该接口 封装为一个FutureTask 任务对象,最后在交给线程。
是一个泛型接口 这里String 是要返回的类型 可以定义为其他类型

package com.cky.file;import java.util.concurrent.Callable;public class MyCallable implements Callable<String> {private int n;public MyCallable(int n) {this.n = n;}@Overridepublic String call() throws Exception {int sum=0;for (int i = 1; i <= n; i++) {sum+=i;}return "1-"+n+"的和为:"+sum;}
}
package com.cky.file;import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;public class Endecode {public static void main(String[] args) throws Exception {//创建一个Callable对象Callable<String> callable=new MyCallable(100);//封装为一个FutureTask对象//FutureTask 是一个任务对象 实现了Runnable接口// 在执行完毕后 可以通过get方法获得执行结果FutureTask<String> f1 = new FutureTask<>(callable);//交给一个Thread对象new Thread(f1).start();//获得执行结果 注意:执行未结束 是不会取得结果的String s = f1.get();System.out.println(s);//1-100的和为:5050}}

在这里插入图片描述

4、与线程有关的操作方法

在这里插入图片描述

package com.cky.file;public class MyThread extends  Thread{public MyThread( String name){super(name);}@Overridepublic void run() {Thread t=Thread.currentThread();for (int i = 0; i <3 ; i++) {System.out.println(t.getName()+i);}}
}
package com.cky.file;import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;public class Endecode {public static void main(String[] args) throws Exception {MyThread myThread1=new MyThread("子线程1");//修改名字
//        myThread1.setName("子线程1");myThread1.start();MyThread myThread2=new MyThread("子线程2");
//        myThread2.setName("子线程2");myThread2.start();//哪个线程在执行 就是哪个线程Thread m = Thread.currentThread();String name = m.getName();System.out.println(name);for (int i = 0; i < 3; i++) {System.out.println(name+"线程"+i);}}}
package com.cky.file;import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;public class Endecode {public static void main(String[] args) throws Exception {
//当输出为3时 ,延缓5秒在运行for (int i = 0; i < 5; i++) {System.out.println(i);if(i==3)Thread.sleep(5000);}}}
package com.cky.file;import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;public class Endecode {public static void main(String[] args) throws Exception {MyThread myThread1=new MyThread("子线程1");myThread1.start();myThread1.join();//当前线程执行完毕 再往下进行MyThread myThread2=new MyThread("子线程2");myThread2.start();myThread2.join();Thread m = Thread.currentThread();String name = m.getName();System.out.println(name);for (int i = 0; i < 3; i++) {System.out.println(name+"线程"+i);}}}

子线程10
子线程11
子线程12
子线程20
子线程21
子线程22
main
main线程0
main线程1
main线程2

5、线程安全问题

5.1 取钱案例

造成线程安全问题原因:
同时存在多个线程,访问同一个共享资源,且都要修改该共享资源。
同时进入钱够的判断,造成线程 不安全

package com.cky.file;public class Account {private double money;public Account(double money) {this.money = money;}public double getMoney() {return money;}public void setMoney(double money) {this.money = money;}public void drawmoney(double money){String name=Thread.currentThread().getName();if (this.money>=money){System.out.println(name+"来取钱成功");this.money-=money;System.out.println(name+"取后,剩余:"+this.money);}else{System.out.println("钱不够");}}
}
package com.cky.file;public class MyThread extends  Thread{private Account account;public MyThread( Account account,String name){super(name);this.account=account;}@Overridepublic void run() {
//取钱account.drawmoney(10000);}}
package com.cky.file;import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;public class Endecode {public static void main(String[] args) {//创建一个共享账户Account account=new Account(10000);//两个线程MyThread myThread1=new MyThread(account,"小红");MyThread myThread2=new MyThread(account,"小明");myThread1.start();myThread2.start();}}

小红来取钱成功
小明来取钱成功
小红取后,剩余:0.0
小明取后,剩余:-10000.0

5.2 线程同步

解决线程安全问题的办法
在这里插入图片描述
在这里插入图片描述

5.2.1 同步代码块

  public void drawmoney(double money){String name=Thread.currentThread().getName();synchronized (this) {if (this.money>=money){System.out.println(name+"来取钱成功");this.money-=money;System.out.println(name+"取后,剩余:"+this.money);}else{System.out.println("钱不够");}}

实例方法 同步代码块 通常加this 代表当前资源 比如小红和小黑共享一个账户 小白和小亮共享一个账户,使用this 保证了 只锁住同一个账户 不被多个访问 但不会去锁住别人的账户。
如果是静态方法 每个类只有一份 只允许一个访问 通常用 类.class 来锁住所有,只允许一个人访问。

   public static void text(){synchronized (Account.class){}}

在这里插入图片描述

5.2.2 同步方法

 public synchronized void drawmoney(double money){String name=Thread.currentThread().getName();if (this.money>=money) {System.out.println(name + "来取钱成功");this.money -= money;System.out.println(name + "取后,剩余:" + this.money);}else{System.out.println("钱不够");}}

在这里插入图片描述

5.2.3 Lock锁

在这里插入图片描述

package com.cky.file;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class Account {private double money;//为每个账户创建一个锁对象private final Lock lock=new ReentrantLock();public Account(double money) {this.money = money;}public double getMoney() {return money;}public void setMoney(double money) {this.money = money;}public void drawmoney(double money){String name=Thread.currentThread().getName();//加锁lock.lock();try {if (this.money>=money) {System.out.println(name + "来取钱成功");this.money -= money;System.out.println(name + "取后,剩余:" + this.money);}else{System.out.println("钱不够");}} catch (Exception e) {e.printStackTrace();}finally {//写在finally里,为了防止上边代码出错,导致没有解锁lock.unlock();}}
}

6、线程池

如果每一个线程都需要我们去新创建一个线程的话,就会耗资源并且耗时(创建线程是一件耗时的事)
此时就需要线程池,线程池可以复用线程,不用经常去创建线程。
并且线程池也可以确定任务队列中任务的个数。防止任务过多,导致内存溢出。
在这里插入图片描述

6.2 创建线程池

在这里插入图片描述

6.2.1 使用ExecutorService创建

在这里插入图片描述
在这里插入图片描述

package com.cky.file;public class MyRunnable implements  Runnable{private int n;public MyRunnable(int n) {this.n = n;}@Overridepublic void run()  {int sum=0;for (int i = 1; i <= n; i++) {sum+=i;}System.out.println(Thread.currentThread().getName()+"-1-"+n+"的和为:"+sum);try {Thread.sleep(100000);} catch (InterruptedException e) {e.printStackTrace();}}
}
package com.cky.file;import java.util.concurrent.*;public class Endecode {public static void main(String[] args) {//创建一个线程池对象/* int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler*/ExecutorService pool=new ThreadPoolExecutor(3,5,8,TimeUnit.SECONDS,new ArrayBlockingQueue<>(4),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());MyRunnable myRunnable=new MyRunnable(100);pool.execute(myRunnable);pool.execute(myRunnable);pool.execute(myRunnable);//开始往任务队列加pool.execute(myRunnable);pool.execute(myRunnable);pool.execute(myRunnable);pool.execute(myRunnable);//开始使用临时线程pool.execute(myRunnable);pool.execute(myRunnable);
//        开始使用任务队列满的措施pool.execute(myRunnable);}}
D:\JAVA\jdk-17.0.8\bin\java.exe "-javaagent:D:\SOftware\idea\IntelliJ IDEA 2021.3.2\lib\idea_rt.jar=49289:D:\SOftware\idea\IntelliJ IDEA 2021.3.2\bin" -Dfile.encoding=UTF-8 -classpath E:\java_code\project\out\production\hello-app;E:\java_code\project\hello-app\lib\dom4j-2.1.4.jar;E:\java_code\project\hello-app\lib\commons-io-2.15.1.jar com.cky.file.Endecode
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@5b480cf9[Not completed, task = java.util.concurrent.Executors$RunnableAdapter@5f184fc6[Wrapped task = com.cky.file.MyRunnable@3feba861]] rejected from java.util.concurrent.ThreadPoolExecutor@6f496d9f[Running, pool size = 5, active threads = 5, queued tasks = 4, completed tasks = 0]at java.base/java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2065)at java.base/java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:833)at java.base/java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1365)at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:123)at com.cky.file.Endecode.main(Endecode.java:31)
pool-1-thread-1-1-100的和为:5050
pool-1-thread-2-1-100的和为:5050
pool-1-thread-5-1-100的和为:5050
pool-1-thread-3-1-100的和为:5050
pool-1-thread-4-1-100的和为:5050

//我们可以看到 使用了临时线程 并且使用了任务满时的策略。

新任务策略

在这里插入图片描述

package com.cky.file;import java.util.concurrent.Callable;public class MyCallable implements Callable<String> {private int n;public MyCallable(int n) {this.n = n;}@Overridepublic String call() throws Exception {int sum=0;for (int i = 1; i <= n; i++) {sum+=i;}return Thread.currentThread().getName()+"-1-"+n+"的和为:"+sum;}
}
package com.cky.file;import java.util.concurrent.*;public class Endecode {public static void main(String[] args) throws ExecutionException, InterruptedException {//创建一个线程池对象/* int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler*/ExecutorService pool=new ThreadPoolExecutor(3,5,8,TimeUnit.SECONDS,new ArrayBlockingQueue<>(4),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());Future<String> f1 = pool.submit(new MyCallable(100));Future<String> f2 = pool.submit(new MyCallable(200));Future<String> f3 =pool.submit(new MyCallable(300));Future<String> f4 =pool.submit(new MyCallable(400));System.out.println(f1.get());System.out.println(f2.get());System.out.println(f3.get());System.out.println(f4.get());}}

pool-1-thread-1-1-100的和为:5050
pool-1-thread-2-1-200的和为:20100
pool-1-thread-3-1-300的和为:45150
pool-1-thread-3-1-400的和为:80200

6.2.2 使用Executors工具类创建

在这里插入图片描述
在这里插入图片描述
创建单个线程池

   ExecutorService pool = Executors.newSingleThreadExecutor();pool.execute(new MyRunnable(100));

使用该种方式的风险
在这里插入图片描述

核心线程数量问题

如果时IO密集型 则一般配置 cpu数量*2
如果是计算密集型 则一般是 cpu数量+1

7、并发、并行,线程生命周期

在这里插入图片描述

并发
在这里插入图片描述
并行
在这里插入图片描述
在这里插入图片描述
生命周期
在这里插入图片描述
在这里插入图片描述

7、乐观锁、悲观锁

悲观锁:一上来就加锁,线程安全,性能较差。
乐观锁:不加锁,等到要开始出现线程安全问题时,才开始控制。

package com.cky.file;import java.util.concurrent.atomic.AtomicInteger;public class MyRunnable implements  Runnable{private AtomicInteger count=new AtomicInteger();@Overridepublic void run()  {for (int i = 0; i <100 ; i++) {System.out.println(Thread.currentThread().getName()+"count===>"+count.incrementAndGet());}}
}
package com.cky.file;import java.util.concurrent.*;public class Endecode {public static void main(String[] args) throws ExecutionException, InterruptedException {//乐观锁//100个线程 对一个变量 各加100次MyRunnable myRunnable=new MyRunnable();for (int i = 0; i <100 ; i++) {new Thread(myRunnable).start();}}}

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

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

相关文章

3.21小题总结

第一题&#xff1a;生日蛋糕 题解&#xff1a;这题是蛋糕结构是一层一层的&#xff0c;估计很多人很快就能想到是dfs&#xff0c;但是这题的难想的点在于 你每层的状态该怎么去确定&#xff0c;你怎么来确定每层的半径和高度是多少&#xff0c;一开始我也不知很理解&#xff0…

82.删除排序链表中的重复元素II

给定一个已排序的链表的头 head &#xff0c; 删除原始链表中所有重复数字的节点&#xff0c;只留下不同的数字 。返回 已排序的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,3,4,4,5] 输出&#xff1a;[1,2,5]示例 2&#xff1a; 输入&#xff1a;head [1,1,1,2…

贝尔曼方程【Bellman Equation】

强化学习笔记 主要基于b站西湖大学赵世钰老师的【强化学习的数学原理】课程&#xff0c;个人觉得赵老师的课件深入浅出&#xff0c;很适合入门. 第一章 强化学习基本概念 第二章 贝尔曼方程 文章目录 强化学习笔记一、状态值函数贝尔曼方程二、贝尔曼方程的向量形式三、动作值…

刷题28-30(力扣0322/0078/0221)

0322. 零钱兑换 题目&#xff1a; 给你一个整数数组 coins &#xff0c;表示不同面额的硬币&#xff1b;以及一个整数 amount &#xff0c;表示总金额。计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额&#xff0c;返回 -1 。你可以…

【微服务】Nacos配置管理

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;微服务 ⛺️稳中求进&#xff0c;晒太阳 Nacos除了可以做注册中心&#xff0c;同样可以做配置管理来使用。 1.统一配置管理 当微服务部署的实例越来越多&#xff0c;达到数十、数百时&am…

DPDK-RCU的简明使用

文章目录 摘要RCU的基本概念DPDK RCU Library的使用其他 摘要 本文主要介绍DPDK中RCU Library的使用。 在使用这个库之前&#xff0c;我们先了解RCU的基本概念。 掌握RCU的基本概念后&#xff0c;便可轻松的使用这个库。 RCU的基本概念 参考&#xff1a;Linux内核同步机制之…

深度学习模型部署-番外-TVM机器学习编译

什么是机器学习编译器/AI编译&#xff1f; 图片来自知乎大佬的文章 机器学习编译是指&#xff1a;将模型从训练形式转变为部署模式 训练模式&#xff1a;使用训练框架定义的模型部署模式&#xff1a;部署所需要的模式&#xff0c;包括模型每个步骤的实现代码&#xff0c;管理资…

什么是代理IP?TikTok运营需要知道的IP知识

对于运营TikTok的从业者来说&#xff0c;IP的重要性自然不言而喻。 在其他条件都正常的情况下&#xff0c;拥有一个稳定&#xff0c;纯净的IP&#xff0c;你的视频起始播放量很可能比别人高出不少&#xff0c;而劣质的IP轻则会限流&#xff0c;重则会封号。那么&#xff0c;如何…

插入排序+希尔排序

目录 插入排序&#xff1a; 希尔排序&#xff1a; 插入排序&#xff1a; 注意这里不要将插入排序和冒泡排序弄混&#xff1a; 插入排序是将数据不断放入前一个有序数列&#xff1a; // 插入排序 void InsertSort(int* a, int n) {for (int j 1; j < n; j){for (int i j;…

Java类的多态作用及解析

多态是面向对象编程中一个重要的特性。简单来说&#xff0c;多态就是指同一个方法在不同的对象上有不同的实现。通过多态&#xff0c;我们可以在运行时根据对象的实际类型来动态地调用相应的方法&#xff0c;从而提高代码的灵活性和可扩展性。 以下是 Java 类中多态的一些作用…

如何用HBuider x网页制作蜡笔小新

目录 下载软件 ​编辑 一.制作蜡笔小新个人介绍界面 二.制作蜡笔小新我的偶像界面 三.制作蜡笔小新我的家乡界面 四.制作蜡笔小新会员注册界面 下载软件 一、HBuilder IDE的下载 HBuilder下载官网地址&#xff1a;http://www.pc6.com/mac/140609.htmlHBuilderX官方电脑版…

【机器学习-07】逻辑回归(Logistic Regression)的介绍和python实现

Logistic Regression 虽然被称为回归&#xff0c;但其实际上是分类模型&#xff0c;并常用于二分类。主要用来表示某件事情发生的可能性&#xff0c;因此因变量的范围在 0 和 1 之间。Logistic Regression 因其简单、可并行化、可解释强深受工业界喜爱。例如&#xff0c;探讨引…

前端静态开发案例-基于H5C3开发的仿照视频网站的前端静态页面-2 样式表部分和效果展示

原创作者&#xff1a;田超凡&#xff08;程序员田宝宝&#xff09; 版权所有&#xff0c;引用请注明原作者&#xff0c;严禁复制转载 charset "utf-8"; /* 程序员田宝宝原创版权所有&#xff0c;仿冒必究&#xff0c;该界面是仿照某视频网站官网开发的静态页面 */ …

基于Jenkins + Argo 实现多集群的持续交付

作者&#xff1a;周靖峰&#xff0c;青云科技容器顾问&#xff0c;云原生爱好者&#xff0c;目前专注于 DevOps&#xff0c;云原生领域技术涉及 Kubernetes、KubeSphere、Argo。 前文概述 前面我们已经掌握了如何通过 Jenkins Argo CD 的方式实现单集群的持续交付&#xff0c…

java 继承(下)

前面我们已经说明了什么是继承&#xff1f;继承的好处弊端等&#xff0c;不清楚的可参照链接 java 继承&#xff08;上&#xff09;-CSDN博客 本篇文章主要理解 继承中变量&#xff0c;构造方法&#xff0c;成员方法的访问特点。 1、继承中变量的访问特点 1.1 代码实现 不看…

若依添加页面

背景&#xff1a;我想增加的是一个收支管理的页面 views中直接添加income文件夹&#xff0c;里面放着index.vue 网页的菜单中添加这个页面的菜单

基于springboot的留守儿童爱心网站

技术&#xff1a;springbootmysqlvue 一、系统背景 现代社会&#xff0c;由于经济不断发展&#xff0c;旧物捐赠的数量也在不断的增加&#xff0c;人们对留守儿童爱心信息的需求也越来越高。 以往的留守儿童爱心的管理&#xff0c;一般都是纸质文件来管理留守儿童爱心信息&am…

分布式异步任务框架celery

Celery介绍 github地址&#xff1a;GitHub - celery/celery: Distributed Task Queue (development branch) 文档地址&#xff1a;Celery - Distributed Task Queue — Celery 5.3.6 documentation 1.1 Celery是什么 celery时一个灵活且可靠的处理大量消息的分布式系统&…

Java:设计模式

文章目录 参考简介工厂模式简单工厂模式工厂方法模式抽象工厂模式总结 单例模式预加载懒加载线程安全问题 策略模式 参考 知乎 简介 总体来说设计模式分为三类共23种。 创建型模式&#xff0c;共五种&#xff1a;工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模…

基于浏览器localStorage作为数据库完成todolsit项目

一、文章内容 TodoList结构搭建HTML代码 TodoList样式编写Css代码 TodoList行为表现JavaScript代码 二、项目展示 项目介绍 Todolist是一个基于B/S模式开发的待办事项软件&#xff0c;主要功能是离线记录用户的待办事项和已经完成的事情&#xff0c;基于htmlcssjs实现&am…