线程

1 线程概念

进程:进程指正在内存中运行的程序。进程具有一定的独立性。

线程:线程是进程中的一个执行单元。负责当前进程中程序的执行。一个进程中至少有一个线程。如果一个进程中有多个线程,称之为多线程程序。

java中的线程采用的是抢占式调度,如果线程的优先级相同,那么会随机选择一个线程执行。理论上,优先级高的线程抢到CPU的概率更大。

CPU使用抢占式调度在多个线程间进行着高速的切换。对于CPU的一个核而言,某个时刻,只能执行一个线程。CPU在多个线程间切换的速度相对我们的感受要快得多,看上去就是在同时执行。

多线程程序并不能提高程序的运行速度,但是能够提高程序的运行效率,让CPU的使用率更高。

为什么要使用多线程?

  • 提高用户体验

  • 提高CPU的利用率

    • 线程在执行的过程中会和计算机硬件进行交互,线程和硬件交互的时候会暂时空置CPU,从而多线程可以提高CPU利用率

2 线程的创建

2.1 线程的创建方式一

  • 定义一个类继承Thread

  • 重写run方法

  • 创建子类对象

  • 调用start方法,开启线程并让线程执行

public class MyThread extends Thread{
​@Overridepublic void run() {// 把希望其他线程执行的代码放在run方法中for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + "正在执行了。。。");}}
}

2.2 线程的创建方式二[推荐]

  • 定义类实现Runnable接口

  • 实现接口中的run方法

  • 创建Thread类的对象

  • 将Runnable接口的实现类对象作为参数传递给Thread类的构造方法

  • 调用Thread类的start方法开启线程

public class MyRunnable implements Runnable{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + "正在执行");}}
}
​
​
public static void main(String[] args) {
//        MyRunnable myRunnable = new MyRunnable();
//        Thread thread = new Thread(myRunnable,"子线程");
//        thread.start();// 匿名内部类
//        new Thread(new Runnable() {
//            @Override
//            public void run() {
//                for (int i = 0; i < 100; i++) {
//                    System.out.println(Thread.currentThread().getName() + "执行了");
//                }
//            }
//        }).start();// lambda表达式new Thread(()-> {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + "执行了");}},"小线程").start();
​for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName());}}

课堂练习:售票案例:模拟电影院卖票,实现3个窗口同时卖"哪吒2"的100张票。

方式一:

public class Ticket implements Runnable{int ticket = 100;@Overridepublic void run() {while (true){if (ticket <= 0) {break;}
​System.out.println(Thread.currentThread().getName() + "您的票是" + ticket--);}}
}
​
​
public static void main(String[] args) {Ticket ticket = new Ticket();// 创建线程Thread thread1 = new Thread(ticket,"窗口1");Thread thread2 = new Thread(ticket,"窗口2");Thread thread3 = new Thread(ticket,"窗口3");thread1.start();thread2.start();thread3.start();}

方式二:

public class TicketDemo {
​static int ticket = 100;
​public static void saleTicket(){while (true){if (ticket <= 0) {break;}System.out.println(Thread.currentThread().getName() + "您的票是" + ticket--);}}
}
​
​
​public static void main(String[] args) {
//        new Thread(()->{
//            TicketDemo.saleTicket();
//        },"窗口1").start();new Thread(TicketDemo::saleTicket,"窗口1").start();new Thread(TicketDemo::saleTicket,"窗口2").start();new Thread(TicketDemo::saleTicket,"窗口3").start();}

经过多次运行会发现以上的内容可能会出现以下结果:

  • 有重复的票

  • 有0的票

  • 有-1的票

线程安全隐患:如果有多个线程在同时运行,而这些线程可能会同时运行某段代码。程序每次运行的结果和单线程运行的结果一样,而且其他的变量值也和预期的一样,就是线程安全。否则就是有线程安全隐患。

线程安全隐患出现的条件:

  • 多线程

  • 有共享资源

  • 有修改操作

以上三个条件破坏一个就可以避免线程安全隐患

3 线程安全隐患解决方案

3.1 同步代码块

synchronized(锁对象){可能会产生线程安全问题的代码;
}

在同一个时刻,同步代码块中只能有一个线程执行,执行完毕之后会释放锁,所有线程再去抢锁对象,抢到之后的线程可以执行同步代码块中的代码,其他线程等待同步代码块执行完毕后释放锁。

同步代码块中的锁对象可以是任意的对象,但是多个线程时,要使用同一个锁对象才能够保证线程安全。

3.2 同步方法

在方法上添加synchronized

public synchronized 返回值类型  方法名(参数){}

在同一个时刻,同步方法中只能有一个线程执行,执行完毕之后会释放锁,所有线程再去抢锁对象,抢到之后的线程可以执行同步方法中的代码,其他线程等待同步方法执行完毕后释放锁。

// 静态同步方法// 锁对象是  类名.classprivate synchronized void method2(){}
​// 同步方法 // 锁对象是thisprivate synchronized boolean method() {if (ticket <= 0) {return true;}
​System.out.println(Thread.currentThread().getName() + "您的票是" + ticket--);return false;}

3.3 Lock

// 创建锁Lock lock = new ReentrantLock();
​@Overridepublic void run() {while (true) {// 线程休眠  单位:毫秒try {Thread.sleep(1L);} catch (InterruptedException e) {throw new RuntimeException(e);}// 加锁lock.lock();try {if (ticket <= 0) {break;}System.out.println(Thread.currentThread().getName() + "您的票是" + ticket--);} finally {// 释放锁lock.unlock();}
​}}

同步:一段代码只允许一个线程执行

异步:一段代码允许多个线程执行

同步一定是线程安全的

线程安全不一定同步

异步不一定线程不安全

线程不安全一定是异步

3.4 ThreadLocal

  • 从程序上游向下游传递数据

static    ThreadLocal<String> threadLocal = new ThreadLocal<>();public static void main(String[] args) {String str = "你好";threadLocal.set(str);a();}public static void a(){b();}private static void b() {c();}private static void c() {d();}private static void d() {String s = threadLocal.get();System.out.println(s);}

  • 可以解决线程安全隐患

public static ThreadLocal<Printer> tl = new ThreadLocal<>() {// 匿名内部类// 重写初始化方法,返回一个打印机@Overrideprotected Printer initialValue() {return new Printer();}};public static void main(String[] args) {new Thread(new GFS()).start();new Thread(new BFM()).start();}}class Printer {public void print(String str) {System.out.println("打印机在打印" + str);}
}class BFM implements Runnable {@Overridepublic void run() {Printer printer = TestDemo4.tl.get();printer.print(printer + "我是白富美");printer.print(printer + "我很美");printer.print(printer + "很有钱");}
}class GFS implements Runnable {@Overridepublic void run() {Printer printer = TestDemo4.tl.get();printer.print(printer + "我是高富帅");printer.print(printer + "我很高");printer.print(printer + "也很帅");}
}

4 死锁

死锁:由于锁的嵌套导致锁之间相互锁死的现象

public class TestDemo5 {static Print p = new Print();static Scan s = new Scan();public static void main(String[] args) {new Thread(()->{synchronized (p){p.print();try {Thread.sleep(1000L);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (s){s.scan();}}}).start();new Thread(()->{synchronized (s){s.scan();synchronized (p){p.print();}}}).start();}
}class Print{public void print(){System.out.println("打印机在打印");}
}class Scan{public void scan(){System.out.println("扫描仪在扫描");}
}

5 等待唤醒机制

package cn.javasm.demo;public class TestDemo6 {public static void main(String[] args) {Student student = new Student();new Thread(new Ask(student)).start();new Thread(new Change(student)).start();}
}class Change implements Runnable{private Student student;public Change(Student student) {this.student = student;}@Overridepublic void run() {while (true){synchronized (student){if (student.flag){try {// 让当前线程陷入等待student.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}if ("周杰伦".equals(student.getName())){student.setName("林志玲");student.setGender("女生");}else {student.setName("周杰伦");student.setGender("男生");}student.flag = true;// 随机唤醒一个正在等待的线程student.notify();}}}
}class Ask implements Runnable{private Student student;public Ask(Student student) {this.student = student;}@Overridepublic void run() {while (true){synchronized (student) {if (!student.flag){try {// 让当前线程陷入等待student.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("老师,我是" + student.getName() + ",我是" + student.getGender() + ",我要问问题");student.flag = false;// 唤醒一个线程student.notify();}}}
}class Student{private String name;private String gender;// true让Ask执行// false让Change执行public boolean flag;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}
}

作业:完成生产者消费者模式

一个线程作为消费者,一个线程作为生产者。生产者每生产一次,消费者就消费一次。生产者每次生产的商品数量以及消费者消费的数量使用随机数产生即可。每一次的生产的商品数量和上一次剩余的商品数量之和不能超过1000。

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

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

相关文章

虚拟机CentOS里JDK的安装与环境配置

---本文以JDK17为例---步骤 1&#xff1a;进入/tmp临时目录# 进入临时目录 cd /tmp步骤 2&#xff1a;下载 Java 17 安装包wget https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.9%2B9/OpenJDK17U-jdk_x64_linux_hotspot_17.0.9_9.tar.gz步骤 3&am…

mybatis-plus多租户兼容多字段租户标识

默认租户插件处理器的缺陷 在springboot工程中引入mybatis-plus的租户插件TenantLineInnerInterceptor&#xff0c;能简化我们的数据隔离操作&#xff0c;例如各类含租户用户登录权限的rest接口中&#xff0c;不需要再根据登录用户-set租户条件-触发查询&#xff0c;租户插件能…

HBase高级特性(布隆过滤器和协处理器)、列族设计、rowkey设计以及热点问题处理

在阐述HBase高级特性和热点问题处理前&#xff0c;首先回顾一下HBase的特点&#xff1a;分布式、列存储、支持实时读写、存储的数据类型都是字节数组byte[]&#xff0c;主要用来处理结构化和半结构化数据&#xff0c;底层数据存储基于hdfs。 同时&#xff0c;HBase和传统数据库…

redis sentinel 与 clauster 的区别

Redis Sentinel(哨兵)和Redis Cluster(集群)是Redis提供的两种不同的高可用和扩展性解决方案,它们的设计目标和适用场景有显著区别: 1. 核心功能与目标 Redis Sentinel 主要解决主从架构的高可用问题,实现自动故障转移 监控主从节点状态,当主节点故障时自动将从节点提…

MySQL数据库中快速导入大数据sql

1.PwerShell命令页面导入全表数据库 -P3310 指定数据库端口号Get-Content "本地sql文件目录" | .\mysql -u root -p -P 33102.PwerShell命令页面导入单表到数据库 -P3310 指定数据库端口号Get-Content "本地sql文件目录" | .\mysql -u root -p -P 3310 数…

消息类型proto的编写和生成

消息类型proto的编写和生成 代码如下&#xff1a; syntax"proto3"; package xypmq;enum ExchangeType {UNKNOWNTYPE0;DIRECT1;FANOUT2;TOPIC3; };enum DeliveryMode {UNKNOWNMODE0;UNDURABLE1;DURABLE2; };message BasicProperties {string id1;DeliveryMode deliver…

Vuetify:构建优雅Vue应用的Material Design组件库

Vuetify是一个基于Material Design设计规范的Vue.js UI组件库&#xff0c;它提供了80多个精心设计的组件&#xff0c;帮助开发者快速构建美观且功能丰富的企业级应用。核心特性1. 完整的Material Design实现// 所有组件遵循Material Design规范 <v-btn color"primary&q…

SpringBoot 注解深剖:@RequestParam 与 @RequestBody 的终极对决,90% 的开发者都踩过这些坑!

在 SpringBoot 开发中&#xff0c;处理 HTTP 请求参数是我们每天都要面对的工作。而RequestParam和RequestBody这两个注解&#xff0c;就像是我们手中的两把利剑&#xff0c;既能高效解决问题&#xff0c;用不好也可能 "误伤" 自己。作为一名资深 Java 开发者&#x…

【Docker】P2 Docker环境构建准备:MacOS 与 Linux

目录操作系统与 Docker 的兼容性分析Docker 技术本质MacOS 环境下的 Docker 构建1. 安装前准备2. Docker Desktop安装3. 镜像加速配置高级操作&#xff1a;文件共享配置Linux 环境下的 Docker 构建卸载历史版本配置软件源Docker 核心组件安装系统服务配置镜像加速器配置应用配置…

OpenCV 发票识别全流程:透视变换与轮廓检测详解

目录 前言 一、核心技术原理&#xff1a;透视变换与轮廓检测 1. 透视变换&#xff1a;让倾斜发票 “正过来” &#xff08;1&#xff09;什么是透视变换&#xff1f; &#xff08;2&#xff09;透视变换的 5 个关键步骤 2. 轮廓检测&#xff1a;精准定位发票区域 &#x…

并发:使用volatile和不可变性实现线程安全

《Java并发编程实战》中的VolatileCachedFactorizer展示了如何使用volatile和不可变性来实现线程安全。解决了简单缓存实现中可能出现的线程安全问题&#xff0c;同时避免了全量同步带来的性能开销。 场景背景 假设有一个服务&#xff08;如因数分解服务&#xff09;&#xff0…

Linux x86 stability和coredump

1 POSIX pthread_create原理 1&#xff09;fork()、pthread_create()、vfork()对应的系统调用分别是sys_fork()、sys_clone()、sys_vfork()&#xff0c;它们在内核中都是通过do_fork()实现的。 2&#xff09;系统中所有的进程都组织在init_task.tasks链表下面&#xff0c;每个进…

【PyTorch】多对象分割

对象分割任务的目标是找到图像中目标对象的边界。实际应用例如自动驾驶汽车和医学成像分析。这里将使用PyTorch开发一个深度学习模型来完成多对象分割任务。多对象分割的主要目标是自动勾勒出图像中多个目标对象的边界。 对象的边界通常由与图像大小相同的分割掩码定义&#xf…

RabbitMQ---面试题

总结我们所学内容&#xff0c;这里推荐博客进行复习 RabbitMQ---面试题_rabbitmq常问面试题-CSDN博客

MasterGo自动布局(Auto Layout)

自动布局是用来表示 子元素与子元素之间互相影响的一种排版方式,是一种响应式布局技术。一般是将所有元素设计完成后再使用自动布局进行设置。 自动布局就是响应式布局,就是在不同尺寸的手机上宽度不同都应该怎么展示。 一般页面的一级元素使用约束进行相对定位,二级元素及里…

还在重启应用改 Topic?Spring Boot 动态 Kafka 消费的“终极形态”

场景描述&#xff1a; 你的一个微服务正在稳定地消费 Kafka 的 order_topic。现在&#xff0c;上游系统为了做业务隔离&#xff0c;新增加了一个 order_topic_vip&#xff0c;并开始向其中投递 VIP 用户的订单。你需要在不重启、不发布新版本的情况下&#xff0c;让你现有的消费…

使用vllm部署neo4j的text2cypher-gemma-2-9b-it-finetuned-2024v1模型

使用vllm部署neo4j的text2cypher-gemma-2-9b-it-finetuned-2024v1模型 系统环境准备 由于使用的基于 nvcr.io/nvidia/cuda:12.1.1-cudnn8-runtime-ubuntu22.04 的 workbench,需要进行以下准备(其他系统环境可忽略) ldconfig -p | grep libcudnn 找到 libcudnn 的so库,然…

Coze源码分析-资源库-创建知识库-前端源码-核心组件

概述 本文深入分析Coze Studio中用户创建知识库功能的前端实现。该功能允许用户在资源库中创建、编辑和管理知识库资源&#xff0c;为开发者提供了强大的知识管理和数据处理能力。通过对源码的详细解析&#xff0c;我们将了解从资源库入口到知识库配置弹窗的完整架构设计、组件…

基于时空数据的网约车订单需求预测与调度优化

一、引言随着共享出行行业的蓬勃发展&#xff0c;网约车已成为城市交通的重要组成部分。如何精准预测订单需求并优化车辆调度&#xff0c;是提升平台运营效率、改善用户体验的关键。本文提出一种基于时空数据的网约车订单需求预测与调度优化方案&#xff0c;通过网格化城市空间…

数据结构 Java对象的比较

在Java中&#xff0c;凡是涉及到比较的&#xff0c;可以分为两类情况&#xff1a;一类是基本数据类型的比较&#xff0c;另一类是引用数据类型的比较。对于基本数据类型的比较&#xff0c;我们通过关系运算符&#xff08;、>、<、!、>、<&#xff09;进行它们之间的…