多线程(JAVA笔记第三十九期)

p.s.这是萌新自己自学总结的笔记,如果想学习得更透彻的话还是请去看大佬的讲解

目录

  • 进程和线程的概念
  • 并发与并行
  • 多线程的实现方式
    • 继承Thread类的方式进行实现
    • 使用Runnable接口的方式进行实现
    • 使用Callable接口和Future接口方式进行实现
  • 多线程常用的成员方法
  • 同步代码块
  • 同步方法
  • lock锁
  • 死锁
  • 等待唤醒机制(生产者和消费者)
    • 服务员代码实现
    • 消费者代码实现
    • 生产者代码实现
    • 测试类
  • 等待唤醒机制(阻塞队列方式实现)
    • 生产者代码实现
    • 消费者代码实现
    • 测试类
  • 线程池
    • 任务
    • 线程池

进程和线程的概念

进程是程序的基本执行实体。一个软件运行后其就是一个进程,
在这里插入图片描述
而线程是操作系统能够进行运算调度的最小单位,它被包含在进程中,是进程的实际运作单位
简单来说线程就是应用软件中相互独立,可以同时运行的功能
但这些功能比较多的时候,就形成了多线程
.
比如360。当360运行的时候其就是一个进程,在这个360进程中我们可以使用其功能,比如清理垃圾、木马查杀。使用一个功能其实就是开启了一个线程,当我们既进行清理垃圾又进行木马查杀的时候其实我们就是开启多线程来在同一时间处理我们需要解决的多个问题

而有了多线程我们就可以让程序同时做多件事情,提高了效率
应用场景,只要想让多个事情同时运行就需要用到多线程。比如软件中的耗时操作、所有的聊天软件、所有的服务器

并发与并行

并发:在同一时刻,有多个指令在单个CPU上交替执行
并行:在同一时刻,有多个指令在多个CPU上同时执行

多线程的实现方式

继承Thread类的方式进行实现

public class Demo1 {public static void main(String[] args) {//多线程的第一种启动方式//1.定义一个类继承Thread//2.重写run方法//3.创建子类的对象,并启动线程MyThread t1 = new MyThread();MyThread t2 = new MyThread();t1.setName("线程1");t2.setName("线程2");//开启线程t1.start();t2.start();}
}
class  MyThread extends Thread{@Overridepublic void run() {//书写线程要执行的代码for (int i = 0; i < 100; i++) {System.out.println(getName()+"你好世界");}}
}

使用Runnable接口的方式进行实现

public class Demo2 {public static void main(String[] args) {//多线程的第二种启动方式//1.定义一个类继承Runnable接口//2.重写run方法//3.创建自己的类的对象//4.创建一个Thread类的对象,并启动线程//3.创建MyRun的对象//表示多线程要执行的任务MyRun mr = new MyRun();Thread t1 = new Thread(mr);Thread t2 = new Thread(mr);//给线程设置名字t1.setName("线程1");t2.setName("线程2");t1.start();t2.start();}
}
class MyRun implements Runnable{@Overridepublic void run() {//书写线程要执行的代码for (int i = 0; i < 100; i++) {//获取到当前线程的对象Thread thread = Thread.currentThread();System.out.println(thread.getName()+"你好世界");}}
}

使用Callable接口和Future接口方式进行实现

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class Demo3 {public static void main(String[] args) throws ExecutionException, InterruptedException {//多线程的第三种实现方式//特点:可以获取到多线程运行的结果//1.定义一个类继承Callable接口//2.重写call方法(有返回值,即多线程运行的结果)//3.创建自己的类的对象//4.创建FutureTask的对象(作用管理多线程运行的结果)//5.创建一个Thread类的对象,并启动线程//3.创建MyCallable的对象//表示多线程要执行的任务MyCallable mc = new MyCallable();//4.创建FutureTask的对象(作用管理多线程运行的结果)FutureTask<Integer> ft = new FutureTask<>(mc);//创建线程的对象Thread t1 = new Thread(ft);Thread t2 = new Thread(ft);t1.start();//获取多线程的结果Integer result = ft.get();System.out.println(result);}
}
class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {//求1-100之间的和int sum = 0;for (int i = 0; i <= 100; i++) {sum+=i;}return sum;}
}

在这里插入图片描述

多线程常用的成员方法

在这里插入图片描述
注意
setName方法如果我们没有给线程设置名字,线程也是有默认名字的;格式:Thread-x(x序号,从0开始)
如果我们要给线程设置名字,可以用set方法进行设置,也可以使用构造方法进行设置
.
currentThread方法,当JVM虚拟机启动之后,会自动地启动多条线程
其中有一条线程就叫做main线程
其作用就是去调用main方法,并执行里面的代码
在以前我们写的代码其实都是运行在main线程当中
.
sleep方法哪条线程执行到这个方法,那么哪条线程就会在这里停留对应的时间
方法的参数:就表示睡眠的时间,单位毫秒
当时间到了之后,线程就会自动地醒来,继续执行下面的代码
.
线程的优先级分为10级(1-10),默认为5。优先级越大,先运行的概率越高(也就是有可能优先级低的先运行完毕,但概率很小)
.
当其他的非守护线程执行完毕后,守护线程会陆续结束
.
礼让线程用于使多线程的结果尽量更均匀
.
插入线程用于使某线程在插入到某线程之前执行

在这里插入图片描述

同步代码块

同步代码块,即把操作共享数据的代码锁起来
格式

synchronized (){操作共享数据的代码}

锁默认打开,,当有一个线程进去了,锁就会默认关闭
当里面的代码全部执行完毕,线程出来,锁就会自动打开了
锁对象一定要是唯一的

距离

import static study.threaded.Demo1.obj;
import static study.threaded.Demo1.sum;public class Demo1 {static int sum = 0;public static void main(String[] args) {MyThread1 t1 = new MyThread1();MyThread1 t2 = new MyThread1();t1.setName("线程1");t2.setName("线程2");//开启线程t1.start();t2.start();}
}
class MyThread1 extends Thread{@Overridepublic void run() {while (true){synchronized (MyThread.class){//synchronized要写在循环里面if (sum<100){try {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}sum++;System.out.println(getName()+":"+sum);}else {break;}}}}
}

同步方法

就是将synchronized关键字加到方法上
格式修饰符 synchronized 返回值类型 方法名(方法参数){...}

同步方法是锁住方法里面的所有代码,
并且锁对象不能自己指定:非静态方法的锁为this,静态方法则为当前类的字节码文件对象

lock锁

虽然我们可以理解同步代码块和同步方法的锁对象问题
但是我们并没有直接看到在哪里上了锁,在哪里释放了锁
为了更加清晰地表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
Lock实现提供了更广泛的锁定操作:
比如void lock(); 上锁、void unlock(); 开锁
Lock是一个接口不能直接实例化,要采用其实现类ReentrantLock来实例化

举例

package study.threaded;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;import static study.threaded.Demo1.sum;public class Demo5 {static int sum = 0;public static void main(String[] args) {MyThread2 t1 = new MyThread2();MyThread2 t2 = new MyThread2();t1.setName("线程1");t2.setName("线程2");//开启线程t1.start();t2.start();}
}
class MyThread2 extends Thread{//用static修饰,让所有线程都共同使用这个lock对象static Lock lock = new ReentrantLock();@Overridepublic void run() {while (true){lock.lock();try {if (sum<100){Thread.sleep(50);sum++;System.out.println(getName()+":"+sum);//lock.unlock();这里上锁行不通}else {//lock.unlock();这里上锁行不通break;}} catch (InterruptedException e) {throw new RuntimeException(e);}finally {//在finally里写,保证锁一定会被释放lock.unlock();}}}}

死锁

死锁,即锁的嵌套
这种情况是一个错误,死锁会导致两个线程都一直等着对方先释放锁,则会导致程序卡在这里
详细解释看这里

等待唤醒机制(生产者和消费者)

生产者消费者机制是一个非常经典的多线程协作的模式
其结果为两条线程交替执行
.
其中的一条线程称为生产者,负责生产数据;另一条为消费者,负责消费数据
如果将生产者比作厨师、消费者比作顾客,那的等待唤醒机制的核心就是二者之间传递食物(数据)的服务员
.
理想情况下刚开始服务员应该在厨师处(即生产者抢到了CPU的执行权),然后厨师将食物(数据)给服务员,服务员再递交给顾客,顾客吃完后(处理完数据)服务员又回到厨师处循环往复
.
第二种情况 (消费者等待) 是一开始服务员在顾客处,这时顾客只能等待(wait)服务员上菜,结果等太久睡着了。当厨师把菜做好给服务员后,看到顾客睡着了,便用大嗓门将顾客唤醒(notify)了。
这种情况下,消费者要进行判断:服务员如果没有上菜就等待(结果等睡着了)。而生产者则需要:做菜、把菜给服务员、最后唤醒顾客
.
第三种情况 (生产者等待) 是一开始服务员在厨师那里,厨师做好菜后交给服务员,但结果服务员端着菜出去之后不知道为什么又回到了厨师处。没办法,厨师也只能等待(wait)服务员又一次去上菜,结果也睡着了。顾客吃完后还没饱,看到厨师也睡着了,于是将厨师 唤醒(notify)了。
.
这种情况下,生产者要进行判断:服务员如果手上有菜就等待(结果等睡着了),没有就开始做菜。而消费者则需要:吃菜、唤醒厨师
.
所以等待唤醒机制的双方的流程为:
生产者:1.判断服务员手上是否有菜,有则等待,无则做菜;2.把菜给服务员;3.唤醒顾客吃菜
消费者:1.判断服务员手上是否有菜,有则吃菜,无则等待;2.吃完唤醒厨师做菜
.
等待与唤醒的方法为
在这里插入图片描述

服务员代码实现

public class Waiter {//作用:控制生产者和消费者的执行//有食物为1,无食物为0public static int foodFlog = 0;//顾客最多能吃的食物数量public static int count = 10;//锁对象public static Object lock = new Object();
}

消费者代码实现

public class Foodie extends Thread{@Overridepublic void run() {//书写多线程的步骤//1.循环//2.同步代码块//3.判断共享数据是否到了末尾(到了末尾)//4.判断共享数据是否到了末尾(没有到末尾,执行核心思想)while (true){synchronized (Waiter.lock){if (Waiter.count == 0){break;}else {//先判断服务员是否有食物if (Waiter.foodFlog == 0 ){//没有就等待try {Waiter.lock.wait();//让当前线程跟锁进行绑定} catch (InterruptedException e) {throw new RuntimeException(e);}}else {//有就吃//把吃的总数-1Waiter.count--;//有就吃System.out.println("顾客正在吃食物,还能吃"+Waiter.count+"份");//吃完之后唤醒厨师继续做Waiter.lock.notifyAll();//修改服务员状态Waiter.foodFlog = 0;}}}}}
}

生产者代码实现

public class Cook extends Thread{@Overridepublic void run() {//书写多线程的步骤//1.循环//2.同步代码块//3.判断共享数据是否到了末尾(到了末尾)//4.判断共享数据是否到了末尾(没有到末尾,执行核心思想)while (true){synchronized (Waiter.lock){if (Waiter.count==0){break;}else {//判断桌子上是否有食物if (Waiter.foodFlog == 1){//如果有则等待try {Waiter.lock.wait();//让当前线程跟锁进行绑定} catch (InterruptedException e) {throw new RuntimeException(e);}}else {//没有则制作食物System.out.println("厨师做了一份食物");//修改桌子上的食物状态Waiter.foodFlog = 1;//叫醒等待的消费者开吃Waiter.lock.notifyAll();}}}}}
}

测试类

public class ThreadDemo {public static void main(String[] args) {//创建线程的对象Cook cook = new Cook();Foodie foodie = new Foodie();//给线程设置名字cook.setName("顾客");foodie.setName("厨师");//开启线程cook.start();foodie.start();}
}

等待唤醒机制(阻塞队列方式实现)

阻塞队列方式实现,即去除了服务员,改为一条传送带来实现食物的传递
在这里插入图片描述
可以自定义传送带上最多放的食物数量
在这里插入图片描述

生产者代码实现

package study.threaded;import java.util.concurrent.ArrayBlockingQueue;public class Cook extends Thread{ArrayBlockingQueue<String> queue;public Cook(ArrayBlockingQueue<String> queue) {this.queue = queue;}@Overridepublic void run() {while (true){//不断地把食物放在阻塞队列当中try {queue.put("食物");System.out.println("厨师放了一碗面条");} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

消费者代码实现

package study.threaded;import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;public class Foodie extends Thread{ArrayBlockingQueue<String> queue;public Foodie(ArrayBlockingQueue<String> queue) {this.queue = queue;}@Overridepublic void run() {while (true){while (true){//不断地从阻塞队列中获取食物try {String food = queue.take();System.out.println(food);} catch (InterruptedException e) {throw new RuntimeException(e);}}}}
}

测试类

package study.threaded;import java.util.concurrent.ArrayBlockingQueue;public class ThreadDemo {public static void main(String[] args) {//注意:生产者和消费者要使用同一个阻塞队列//阻塞队列要在测试类中创建ArrayBlockingQueue queue = new ArrayBlockingQueue<>(5);//创建线程的对象,并把阻塞队列传递过去Cook cook = new Cook(queue);Foodie foodie = new Foodie(queue);//给线程设置名字cook.setName("顾客");foodie.setName("厨师");//开启线程cook.start();foodie.start();}
}

在这里插入图片描述

线程池

之前写多线程的时候由于使用线程时必须先创建,并且用完后线程就会消失。这样会浪费系统资源
于是我们可以将线程存到线程池中,要用时就取出来,并且用完后还不会消失,从而节约了系统资源

当我们将任务给线程池的时候,线程池会自动创建一个线程来执行该任务,执行完后再将线程存回线程池中;在提交第二个任务的时候就不用再创建一个新线程,而是直接使用线程池中已有的线程

线程池的最大容量数可以自定义

线程池主要核心原理
1.创建一个空线程池
2.提交任务时,线程池会创建一个新的线程对象,任务执行完毕后,线程会归还给线程池;下回再提交任务时就不需要创建新的线程了,直接复用已有的线程就行了
3.但是如果提交任务时,线程池中没有空闲的线程,也无法创建新的线程时,任务就会排队等待
在这里插入图片描述

任务

public class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println(Thread.currentThread().getName());}
}

线程池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class Demo6 {public static void main(String[] args) throws InterruptedException {//获取线程池的对象//所有的任务全部执行完毕,关闭线程池ExecutorService pool1 = Executors.newCachedThreadPool();//提交任务pool1.submit(new MyRunnable());Thread.sleep(1000);pool1.submit(new MyRunnable());Thread.sleep(1000);pool1.submit(new MyRunnable());Thread.sleep(1000);pool1.submit(new MyRunnable());Thread.sleep(1000);pool1.submit(new MyRunnable());//销毁线程池pool1.shutdown();}
}

在这里插入图片描述

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

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

相关文章

牛客网刷题 | BC125 序列中整数去重

目前主要分为三个专栏&#xff0c;后续还会添加&#xff1a; 专栏如下&#xff1a; C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读&#xff01; 初来乍到&#xff0c;如有错误请指出&#xff0c;感谢&#xff01; 描述 输入n个整数的序列…

【一文概述】常见的几种内外网数据交换方案介绍

一、内外网数据交换的核心需求 内外网数据交换的需求核心在于“安全、效率、合规”&#xff0c;而应用场景的多样性使得不同企业需要定制化的解决方案。通过结合业务特性和安全等级要求&#xff0c;企业能够选择适合的技术方案来实现高效、安全的内外网数据交换。 1、数据安全…

记:排查设备web时慢时快问题,速度提升100%

问题描述 问题1&#xff1a; 发现web登录界面刷新和登录功能都比较卡&#xff0c;开浏览器控制台看了下&#xff0c;让我很惊讶&#xff0c;居然能这么慢&#xff1a; 公司2个局域网内的表现不同&#xff0c;局域网A中的都比较卡&#xff0c;局域网B中的又不存在该现象。 问…

简单的Java小项目

学生选课系统 在控制台输入输出信息&#xff1a; 在eclipse上面的超级简单文件结构&#xff1a; Main.java package experiment_4;import java.util.*; import java.io.*;public class Main {public static List<Course> courseList new ArrayList<>();publi…

环境和工程搭建

1.案例介绍 1.1 需求 实现⼀个电商平台 该如何实现呢? 如果把这些功能全部写在⼀个服务⾥, 这个服务将是巨⼤的. 巨多的会员, 巨⼤的流量, 微服务架构是最好的选择. 微服务应⽤开发的第⼀步, 就是服务拆分. 拆分后才能进⾏"各⾃开发" 1.2 服务拆分 拆分原则 …

港科夜闻 | 香港科大与荷兰代尔夫特理工大学(TU Delft)建立合作伙伴关系,推动艺术科技教育与研究...

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、香港科大与荷兰代尔夫特理工大学(TU Delft)建立合作伙伴关系&#xff0c;推动艺术科技教育与研究。2024年12月6日&#xff0c;合作伙伴计划正式启动&#xff0c;双方期望透过合作加强艺术科技知识交流&#xff0c;借此推…

电脑游戏运行时问题解析:《Geometry Dash》DLL文件损坏的原因与解决方案

电脑游戏运行时问题解析&#xff1a;《Geometry Dash》DLL文件损坏的原因与解决方案 在探索《Geometry Dash》这款节奏明快、充满挑战的几何世界冒险游戏时&#xff0c;我们或许会遇到一些令人头疼的技术问题&#xff0c;其中之一便是DLL文件损坏。DLL&#xff08;动态链接库&…

爬虫逆向学习(十四):分享一下某数通用破解服务开发经验

阅前须知 这篇博客不是教大家怎么实现的&#xff0c;而且告知大家有这个东西&#xff0c;或者说一种趋势&#xff0c;借此分享自己大致的实现经验。具体的实现我也不好整理&#xff0c;毕竟是在别人的基础上缝缝补补。 前言 使用补环境方式破解过某数的同学都知道&#xff0…

Maven 的下载

目录 1、Maven 官方地址2、下载3、解压4、配置本地仓库 1、Maven 官方地址 https://maven.apache.org/ 2、下载 3、解压 将下载的压缩包解压到任意位置 4、配置本地仓库 在 Maven 的安装目录下新建文件夹&#xff0c;用来当作 Maven 的本地仓库 进入 conf 目录下&#xff…

【HarmonyOS】鸿蒙应用实现手机摇一摇功能

【HarmonyOS】鸿蒙应用实现手机摇一摇功能 一、前言 手机摇一摇功能&#xff0c;是通过获取手机设备&#xff0c;加速度传感器接口&#xff0c;获取其中的数值&#xff0c;进行逻辑判断实现的功能。 在鸿蒙中手机设备传感器ohos.sensor (传感器)的系统API监听有以下&#xf…

《拉依达的嵌入式\驱动面试宝典》—C/CPP基础篇(三)

《拉依达的嵌入式\驱动面试宝典》—C/CPP基础篇(三) 你好,我是拉依达。 感谢所有阅读关注我的同学支持,目前博客累计阅读 27w,关注1.5w人。其中博客《最全Linux驱动开发全流程详细解析(持续更新)-CSDN博客》已经是 Linux驱动 相关内容搜索的推荐首位,感谢大家支持。 《拉…

统一身份安全管理体系的业务协同能力

随着集团企业数字化组织转型深化&#xff0c;各组织机构间业务协同程度提升。研发业务协同、数据驱动生产决策等数字化生产协作工作体系得以展开&#xff0c;企业内数据流转加快。企业对统一身份安全管理体系的业务协同管理和支撑能力要求提升&#xff1a; 统一身份管理流程需…

华为HarmonyOS NEXT 原生应用开发:鸿蒙中组件的组件状态管理、组件通信 组件状态管理小案例(好友录)!

文章目录 组件状态管理一、State装饰器1. State装饰器的特点2. State装饰器的使用 二、Prop装饰器&#xff08;父子单向通信&#xff09;1. Prop装饰器的特点2. Prop装饰器的使用示例 三、Link装饰器&#xff08;父子双向通信&#xff09;1. Link装饰器的特点3. Link使用示例 四…

CoolEdit详细使用和安装教程

Cool Edit Pro主要用于音频录制、编辑、混音和后期处理。Cool Edit Pro 的特点包括&#xff1a; 音频编辑&#xff1a;支持多轨编辑&#xff0c;可以同时处理多个音频文件&#xff0c;支持精确的音频剪切、复制、粘贴等操作。 录音功能&#xff1a;内置强大的录音功能&#xf…

如何量化管理研发团队的技术债务?

在探讨技术债的成因之前&#xff0c;我们需要澄清一些关于技术债起因和本质的普遍误解。 误解一&#xff1a;技术债务等同于劣质代码 那么&#xff0c;什么构成了所谓的「劣质代码」&#xff1f; 所谓的好代码&#xff0c;可能是指那些整洁、不会在未来限制你决策的代码&…

使用layui的table提示Could not parse as expression(踩坑记录)

踩坑记录 报错图如下 原因&#xff1a; 原来代码是下图这样 上下俩中括号都是连在一起的&#xff0c;可能导致解析问题 改成如下图这样 重新启动项目&#xff0c;运行正常&#xff01;

代理 IP 行业现状与未来趋势分析

随着互联网的飞速发展&#xff0c;代理 IP 行业在近年来逐渐兴起并成为网络技术领域中一个备受关注的细分行业。它在数据采集、网络营销、隐私保护等多个方面发挥着重要作用&#xff0c;其行业现状与未来发展趋势值得深入探讨。 目前&#xff0c;代理 IP 行业呈现出以下几个显著…

泷羽Sec学习笔记-zmap搭建炮台

zmap搭建炮台 zmap扫描环境&#xff1a;kali-linux 先更新软件库 sudo apt update 下载zmap sudo apt install zmap 开始扫描(需要root权限) sudo zmap -p 80 -o raw_ips.txt 代码解析&#xff1a; sudo&#xff1a;以超级用户&#xff08;管理员&#xff09;权限运行…

Introduction to NoSQL Systems

What is NoSQL NoSQL database are no-tabular非數據表格 database that store data differently than relational tables 其數據的存儲方式與關係型表格不同 Database that provide a mechanism機制 for data storage retrieval 檢索 that is modelled in means other than …

规则引擎(一)-技术要点

本文是规则引擎的第一篇&#xff0c;首先介绍规则引擎的技术要点&#xff0c;系列后续文章大纲 1. 事实 事实是规则的依据&#xff0c;来源于业务&#xff0c;或是业务实体&#xff0c;或是多个业务实体的汇集 2. 项目 描述规则的项目结构&#xff1b;KIE核心api&#xff1b;s…