Java高级-多线程

多线程

  • 1.线程创建的方法
    • 1.1.方法一 继承Thread类
    • 1.2.方法二 实现Runnable接口
    • 1.3.方法三 实现Callable接口
  • 2.线程安全
    • 2.0.线程不安全的案例
    • 2.1.方式一:同步代码块
    • 2.2.方式二:同步方法
    • 2.3.方式三:Lock锁
  • 3.线程池
    • 3.1.创建线程池
    • 3.2.线程池处理Runnable任务
    • 3.3.线程池处理Callable任务
  • 4.并发和并行

1.线程创建的方法

1.1.方法一 继承Thread类

继承Thread类

  • 1.定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法
  • 2.创建MyThread类的对象
  • 3.调用线程对象的start()方法启动线程

优缺点:

  • 优点:编码简单
  • 缺点:线程类已经继承Thread,无法继承其他类,不利于功能的扩展
public class MyThread extends Thread{@Overridepublic void run() {// 线程的执行任务for (int i = 1; i <= 5; i++) {System.out.println("子线程MyThread -> " + i);}}
}
public class ThreadTest1 {public static void main(String[] args) {// 创建MyThread线程类的对象 代表一个线程Thread thread = new MyThread();// 启动线程(自动执行run方法)thread.start();for (int i = 1; i <= 5; i++) {System.out.println("主线程MainThread -> " + i);}}
}

1.2.方法二 实现Runnable接口

实现Runnable接口

  • 1.定义线程任务类MyRunnable实现Runnable接口,重写run()方法
  • 2.创建MyRunnable任务对象
  • 3.把MyRunnable任务对象交给Thread处理
  • 4.调用线程的start()方法启动线程

优缺点:

  • 优点:任务类只是实现接口,可以继续继承其他类/实现其他接口,拓展性强
  • 缺点:需要多一个Runnable对象
public class MyRunnable implements Runnable{@Overridepublic void run() {for (int i = 1; i <= 5; i++) {System.out.println("子线程MyRunnable-> " + i);}}
}
public class ThreadTest2 {public static void main(String[] args) {// 创建任务对象Runnable target = new MyRunnable();// 把 任务对象 交给 线程对象 处理new Thread(target).start();for (int i = 1; i <= 5; i++) {System.out.println("主线程Main -> " + i);}}
}

匿名内部类的写法

  • 1.创建Runnable的匿名内部类对象
  • 2.再交给Thread线程对象
  • 3.再调用线程对象的start()启动线程
public class ThreadTest2_2 {public static void main(String[] args) {// 1.创建匿名内部类Runnable target = new Runnable() {@Overridepublic void run() {for (int i = 1; i <= 5; i++) {System.out.println("子线程 -> " + i);}}};// 2.再交给Thread线程对象Thread thread = new Thread(target);// 3.启动线程thread.start();for (int i = 1; i <= 5; i++) {System.out.println("主线程Main -> " + i);}}
}

1.3.方法三 实现Callable接口

实现Callable接口

  • 1.创建任务对象
    • 定义一个类实现Callable接口,重写call方法,封装要做的事情,和要返回的数据
    • 把Callable类型的对象封装成FutureTask(线程任务对象)
  • 2.把线程任务对象交给Thread对象
  • 3.调用线程对象的start()启动线程
  • 4.线程执行完毕后,通过FutureTask对象的get方法去获取线程任务的执行结果

优缺点:

  • 优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强;可以在线程执行完毕后获取线程执行的结果
  • 缺点:编码复杂
public class MyCallable implements Callable<String> {private int n;public MyCallable(int n) {this.n = n;}@Overridepublic String call() throws Exception {int sum = 0;// 假设 求1~n的和 并返回for (int i = 1; i <= n; i++) {sum += i;}return "线程求出1~" + n + "的和:" + sum;}
}public class ThreadTest3 {public static void main(String[] args) throws ExecutionException, InterruptedException {// 创建Runnable对象Callable<String> callable = new MyCallable(100);// 封装成FutureTaskFutureTask<String> futureTask = new FutureTask<>(callable);// 交给Thread对象Thread thread = new Thread(futureTask);// 启动线程thread.start();// 获取线程执行完毕后返回的结果String result = futureTask.get();System.out.println("result = " + result);}
}

2.线程安全

2.0.线程不安全的案例

案例:模拟线程安全问题。小红和小明同时取钱(同一个账户)

public class ThreadTest {public static void main(String[] args) {Account account = new Account("ICBC-100", 100000);new DrawThread(account, "小明").start(); // 小明new DrawThread(account, "小红").start(); // 小红}
}
public class DrawThread extends Thread{private final Account account;public DrawThread(Account account, String name) {super(name);this.account = account;}@Overridepublic void run() {account.drawMoney(100000);}
}
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) {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 + "来取钱,余额不足");}}public String getCardId() {return cardId;}public void setCardId(String cardId) {this.cardId = cardId;}public double getMoney() {return money;}public void setMoney(double money) {this.money = money;}
}

2.1.方式一:同步代码块

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

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

注意事项

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

在案例中添加同步代码块 (实例方法使用this作为锁对象)

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

如果要求在静态方法中保证线程安全,同步锁应该是 类名.class

public static void test() {synchronized (类名.class) {// 核心代码}
}

2.2.方式二:同步方法

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

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

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

在案例中添加同步方法

public synchronized void drawMoney(double 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 + "来取钱,余额不足");}
}

底层原理

  • 底层也是与隐式锁对象,锁的范围是整个方法代码
  • 如果方法是实例方法:同步方法默认用 this 作为锁的对象
  • 如果方法是静态方法:同步方法默认用 类名.class 作为锁的对象

2.3.方式三:Lock锁

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

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

public class Account {// 创建一个锁对象private final Lock lock = new ReentrantLock();//略public void drawMoney(double money) {String name = Thread.currentThread().getName();try {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 + "来取钱,余额不足");}} catch (Exception e) {throw new RuntimeException(e);} finally {lock.unlock(); // 解锁}}
}

注意事项

  • 1.用 final 修饰
  • 2.用 try...catch...finally 包裹住加锁与解锁操作,保证即使出现异常,也可确保能解锁

3.线程池

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

不断创建新线程的开销是很大的,并且请求过多时,肯定会产生大量的线程出来

3.1.创建线程池

线程池接口:ExecutorService

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

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,  BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
  • 参数一:corePoolSize 指定线程池的核心线程数量
  • 参数二:maximumPoolSize 线程池的最大线程数量(maximumPoolSize > corePoolSize)
  • 参数三:keepAliveTime 临时线程的存活时间
  • 参数四:unit 指定临时线程存活的时间单位(秒/时/分/天)
  • 参数五:workQueue 指定线程池的任务队列
  • 参数六:ThreadFactory 指定线程池的线程工厂
  • 参数七:RejectedExecutionHandler 指定线程池的任务拒绝策略(线程都在忙,任务队列也满了,新任务来了该怎么处理)
ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 5, 8, TimeUnit.SECONDS,new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
策略 (参数七)详解
ThreadPoolExecutor.AbortPolicy()丢弃任务并抛出RejectedExecutionException异常。默认策略
ThreadPoolExecutor.DiscardPolicy()丢弃任务,但是不抛出异常。(不推荐做法)
ThreadPoolExecutor.DiscardOldestPolicy()抛弃队列等待最久的任务,然后把当前任务加入队列中
ThreadPoolExecutor.CallerRunsPolicy()有主线程负责调用任务的run()方法从二绕过线程池直接执行 (老板亲自执行任务)

注意事项

  • 临时线程什么时候创建
    • 新任务提交时发现核心线程在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程
  • 什么时候开始拒绝新任务
    • 核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始拒绝任务

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

方法名称说明
ExcutorService newFixedThreadPool(int nThreads)创建固定线程数量的线程池
ExcutorService newSingleTHreadExecutor()创建只有一个线程的线程池
ExcutorService newCachedThreadPool()线程数量随着任务增加而增加
ExcutorService newScheduledThreadPool(int corePoolSize)
ExecutorService pool = Executors.newFixedThreadPool(3);
ExecutorService pool = Executors.newSingleThreadExecutor();

核心线程数量到底配置多少

计算密集型的任务:核心线程数量 = CPU的核数 + 1

IO密集型的任务:核心线程数量 = CPU的核数 * 2

3.2.线程池处理Runnable任务

public static void main(String[] args) {// 1.通过ThreadPoolExecutor创建一个线程池对象ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 5, 8, TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());Runnable target = new MyRunnable();// 线程池会自动创建新线程,自动处理这个任务,自动执行pool.execute(target); // 第一个线程pool.execute(target); // 第二个线程pool.execute(target); // 第三个线程pool.execute(target);pool.execute(target);pool.execute(target);pool.execute(target);// 临时线程的创建时机pool.execute(target);pool.execute(target);// 到了新任务拒绝的时机pool.execute(target);// 线程池不会主动关闭,程序会一直运行pool.shutdown(); // 等待线程池任务全部完成后,再关闭线程池//pool.shutdownNow();// 立即关闭线程,不管是否还有任务在执行
}

3.3.线程池处理Callable任务

public class ThreadPoolTest2 {public static void main(String[] args) throws Exception{// 1.通过ThreadPoolExecutor创建一个线程池对象ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 5, 8, TimeUnit.SECONDS,new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());// 2.使用线程处理Callable任务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() = " + f1.get());System.out.println("f2.get() = " + f2.get());System.out.println("f3.get() = " + f3.get());System.out.println("f4.get() = " + f4.get());// 线程池不会主动关闭,程序会一直运行pool.shutdown(); // 等待线程池任务全部完成后,再关闭线程池//pool.shutdownNow();// 立即关闭线程,不管是否还有任务在执行}
}

4.并发和并行

**进程:**正常运行的程序/软件就是一个独立的进程

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

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

并发:

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

并行:

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

多线程是怎么执行的

  • 并发和并行同时执行

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

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

相关文章

B2078 含 k 个 3 的数(洛谷)

题目描述 输入两个正整数 m 和 k&#xff0c;其中 1<m≤&#xff0c;1<k≤15 &#xff0c;判断 m 是否恰好含有 k 个 3&#xff0c;如果满足条件&#xff0c;则输出 YES&#xff0c;否则&#xff0c;输出 NO。 输入格式 输入一行&#xff0c;为两个整数 m,k&#xff0…

揭开Markdown的秘籍:标题|文字样式|列表

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;Markdown指南、网络奇遇记 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. ⛳️Markdown 标题二. ⛳️Markdown 文字样式2.1 &#x1f514;斜体2.2 &…

scss和less的区别

Sass(Scss)、Less 都是 CSS 预处理器&#xff0c;他们定义了一种新的语言&#xff0c;其基本思想是&#xff0c;用一种专门的编程语言为 CSS 增加了一些编程的特性&#xff0c;将 CSS 作为目标生成文件&#xff0c;然后开发者就只要使用这种语言进行 CSS 的编码工作。 为什么要…

快速幂计算 Power Calculus

题目 照搬的题解&#xff0c;重命名了变量 ; -) now >>(maxStep-step)的目的是让剩余步数都用来自*2&#xff0c;看看能不能够到n #include <iostream> using namespace std; int maxStep,n; int nums[50];/// brief 深度遍历查找可行路径&#xff0c;用的递归&…

python智慧养老系统—养老信息服务平台vue

本论文中实现的智慧养老系统-养老信息服务平台将以管理员和用户的日常信息维护工作为主&#xff0c;主要涵盖了系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;养老资讯管理&#xff0c;养生有道管理&#xff0c;养老机构管理&#xff0c;系统管理等功能&#x…

海外云手机——平台引流的重要媒介

随着互联网的飞速发展&#xff0c;跨境电商、短视频引流以及游戏行业等领域正经历着迅猛的更新换代。在这个信息爆炸的时代&#xff0c;流量成为至关重要的资源&#xff0c;而其中引流环节更是关乎业务成功的关键。海外云手机崭露头角&#xff0c;成为这一传播过程中的重要媒介…

Grafana 配置实时开通的LDAP认证-基于AD

介绍 本教程适用于9-10版本的Grafana&#xff0c;域控&#xff08;AD&#xff09;使用Windows Server 2022搭建&#xff0c;域控等级为 2016。 域控域名为 songxwn.com 最终实现AD用户统一认证&#xff0c;统一改密&#xff0c;Grafana用户自动添加。权限由Grafana控制 全局…

Ubuntu22.04 gnome-builder gnome C 应用程序习练笔记(一)

一、序言 gnome-builder构建器是gnome程序开发的集成环境&#xff0c;支持主力语言C, C, Vala, jscript, python等&#xff0c;界面以最新的 gtk 4.12 为主力&#xff0c;将其下版本的gtk直接压入了depreciated&#xff0c;但gtk4.12与普遍使用的gtk3有很大区别&#xff0c;原…

第6章 智能租房——前期准备

学习目标 了解智能租房项目&#xff0c;能够说出项目中各模块包含的功能 熟悉智能租房项目的开发模式与运行机制&#xff0c;能够复述项目的开发模式与运行机制 掌握智能租房项目的创建&#xff0c;能够独立创建智能租房项目 掌握智能租房项目的配置&#xff0c;能够为智能租…

排序算法---堆排序

原创不易&#xff0c;转载请注明出处。欢迎点赞收藏~ 堆排序&#xff08;Heap Sort&#xff09;是一种基于二叉堆数据结构的排序算法。它将待排序的元素构建成一个最大堆&#xff08;或最小堆&#xff09;&#xff0c;然后逐步将堆顶元素与堆的最后一个元素交换位置&#xff0c…

解锁 SpringBoot 强大配置功能

1、前言 在当今的软件开发世界中&#xff0c;配置管理是至关重要的一部分。Spring框架为我们提供了多种配置方式&#xff0c;其中ConfigurationProperties和PropertySources是强大的工具&#xff0c;可以帮助我们轻松管理应用程序的配置信息。 本博客将深入探讨这两个关键注解…

求职|基于Springboot的校园求职招聘系统设计与实现(源码+数据库+文档)

校园求职招聘系统目录 目录 基于Springboot的校园求职招聘系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、用户信息管理 2、企业信息管理 3、公告类型管理 4、公告信息管理 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕设选…

JVM-双亲委派机制

双亲委派机制定义 双亲委派机制指的是&#xff1a;当一个类加载器接收到加载类的任务时&#xff0c;会自底向上查找是否加载过&#xff0c; 再由顶向下进行加载。 详细流程 每个类加载器都有一个父类加载器。父类加载器的关系如下&#xff0c;启动类加载器没有父类加载器&am…

再谈Abel群问题

这个问题是很难的&#xff0c;因为1980年代G.Kolesnik的二变量指数和方法被认为是登封造极&#xff0c;他关于ζ(1/2it) 和 Dirichlet 除数问题的论文1982年刊登在Pacufic.J.Math.,文中列了很多无法验证的方程&#xff0c;真不知道论文怎么能发表。所以1985年意大利E.Bombieri和…

【前端素材】bootstrap5实现通用果蔬商城网页模板Netta Food(电商适用,附源码)

一、需求分析 通用果蔬商城网页是指专门为销售各类果蔬产品而设计的在线商城网页。它提供了一个方便的平台&#xff0c;使用户能够浏览、选择和购买各种果蔬产品。 通用果蔬商城网页通常具有以下功能&#xff1a; 商品展示&#xff1a;网页上展示各类果蔬产品的图片、价格、产…

「递归算法」:合并两个有序链表

一、题目 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4]示例 2&#xff1a; 输入&#xff1a;l1 [], l2 [] 输出&#…

发文新思路!双流卷积!CWT-DSCNN-MSA基于时序特征、cwt小波时频图的双流卷积融合注意力机制的故障识别程序!直接运行!

适用平台&#xff1a;Matlab2023版本及以上 本程序参考中文EI期刊《电力自动化设备》2023年12月29号网络首发文献&#xff1a;《基于格拉姆角场与并行CNN的并网逆变器开关管健康诊断》,此外&#xff0c;在此基础上进一步对模型进行多重改进&#xff0c;每个人都可以构造属于自…

低代码流程引擎在数字设计平台的应用:简化创作流程,提升生产效率

数字设计平台在现代企业中发挥着重要的作用&#xff0c;它们为创作者和开发者提供了一个创新和协作的环境。然而&#xff0c;设计过程中繁琐的编码和复杂的工作流程可能降低生产效率。本文将介绍低代码流程引擎是如何应用于数字设计平台&#xff0c;以实现快速、高效的创作流程…

Android 车载应用开发之车载操作系统

一、前言 到 2030 年,全球电动汽车的销量将超过 7000 万辆,保有量将达到 3.8 亿辆,全球年度新车渗透率有望触及 60% 。这一数据来自国际能源署(IEA)发布的《全球电动汽车展望2023》。 市场趋势和政策努力的双加持下,新能源汽车来势凶猛,燃油车保有量逐年递减。此番景象…

Chrome 沙箱逃逸 -- Plaid CTF 2020 mojo

文章目录 前置知识参考文章环境搭建题目环境调试环境 题目分析附件分析漏洞分析OOBUAF 漏洞利用总结 前置知识 Mojo & Services 简介 chromium mojo 快速入门 Mojo docs Intro to Mojo & Services 译文&#xff1a;利用Mojo IPC的UAF漏洞实现Chrome浏览器沙箱逃逸原文…