分享好友 最新动态首页 最新动态分类 切换频道
Java 学习笔记
2024-12-26 21:51

JVM(Java Virtual Machine, Java虚拟机)是运行 Java 程序的核心组件。它的作用是提供一个平台无关的运行环境,使得开发者可以编写一次代码(编译成字节码),然后在任何支持 JVM 的平台上运行。JVM 使得 Java 程序具有 跨平台性 和 安全性,并负责执行 Java 字节码、内存管理、垃圾回收等工作

Java 学习笔记

JRE (Java Runtime Environment, Java运行环境),包含 JVM 和一些标准的 Java 类库,支持 Java 程序的运行

JDK (Java Development Kit, Java 开发工具包),包含 JRE 和开发 Java 程序所需的工具,如编译器(javac)、调试器(jdb)等



使用编译器 将 Java 源代码(文件)编译成 字节码(文件)

字节码 是可以被 JVM 理解和执行的中间代码
机器码 不能在 JVM 上运行,而是在操作系统上运行

类加载器 将 文件加载到 JVM 的内存中

JVM 使用两种方式执行字节码:

  • 解释执行
    解释器逐行读取字节码并执行,效率低
  • 即时编译(JIT 编译)
    即时编译(Just-In-Time Compilation, JIT),在程序运行时,JVM 会将热点代码(即频繁执行的代码)动态编译成机器码,并缓存这些机器码,以便后续直接执行,避免重复解释执行

Object-Oriented Programming, 面向对象编程

在 Java 中,内存的分配由 堆内存(Heap)和 栈内存(Stack)负责

  • 栈内存
    存储:局部变量、方法调用信息
    生命周期:方法调用时,局部变量在栈中分配空间,方法调用结束时,局部变量被销毁
    特点:空间小

  • 堆内存
    存储:对象、数组
    生命周期:由引用关系决定。只要有引用指向某个对象,它就不会被回收
    特点:空间大


面向对象三大特征(封装继承多态

封装(Encapsulation) 是指将对象的状态(属性)和行为(方法)绑定在一起,并通过访问控制机制对外界进行限制,使得对象的内部实现细节对外界不可见,只提供对外的操作接口
封装的核心思想是 数据隐藏,即将对象的内部数据和实现细节隐藏起来,只暴露必要的接口给外部使用。外部代码不直接操作对象的内部数据,而是通过对象提供的公共方法来间接访问和修改数据

实现:



继承(Inheritance)允许一个类(子类)从另一个类(父类)继承属性和方法。通过继承,子类能够重用父类的代码,并且可以扩展或修改父类的行为,形成类之间的层次结构



多态(Polymorphism) 指的是同一个方法或操作,可以作用于不同类型的对象,表现出不同的行为。通过多态,同一操作可以在不同对象上表现出不同的行为

多态主要分为两种类型:

  • 编译时多态(静态多态):也称为 方法重载(Method Overloading)
  • 运行时多态(动态多态):也称为 方法重写(Method Overriding)




多态的实现需要使用向上转型(Upcasting)和向下转型(Downcasting)

  • 向上转型
  • 向下转型

重载(Overloading) 重写(Overriding) 定义 同一类中方法名相同,参数列表不同 子类中重新定义父类的方法 阶段 编译时(静态多态) 运行时(动态多态) 方法签名 方法名相同,参数列表必须不同 方法名、参数列表、返回值类型完全相同 访问修饰符 任意 不能比父类方法的访问权限更低 继承 不需要 必须有

创建不依赖于类的实例对象的变量或方法。使得这些成员可以在类加载时直接访问,而不需要先创建该类的对象



public protected 默认 private 同一类中 √ √ √ √ 同一包中的类 √ √ √ 不同包的子类 √ √ 不同包中的无关类 √

final:不可改变,最终的含义。可以用于修饰类、方法和变量

  • 类:被修饰的类,不能被继承
  • 方法:被修饰的方法,不能被重写
  • 变量:被修饰的变量,有且仅能被赋值一次

抽象类 是一个无法实例化的类,它可以包含抽象方法和非抽象方法。抽象类的目的是为了让子类继承并实现抽象方法,通常用于为其他类提供一个模板或基础

抽象方法 :没有方法体的方法
抽象类:包含抽象方法的类



接口(Interface)是一种特殊的引用类型,它类似于类,但接口只包含常量(static final 变量)和方法声明(abstract 方法),没有方法的实现。接口可以通过 implements 关键字在类中实现,类继承接口并提供接口 所有方法 的具体实现

接口中的变量默认被修饰
方法默认被修饰
需要实现某个接口的类,使用关键字


Q:如果一个接口中,有10个抽象方法,但是我在实现类中,只需要用其中一个,该怎么办?
A:可以在接口跟实现类中间,新建一个中间类(适配器类),让这个适配器类去实现接口,对接口里面的所有的方法做 空重写。让子类继承这个适配器类,想要用到哪个方法,就重写哪个方法。因为中间类没有什么实际的意义,所以一般会把中间类定义为抽象的,不让外界创建对象


抽象类 接口 构造方法 可以有 不能有 方法 抽象方法 和 具体方法 抽象方法(Java 8 后可以有 默认方法) 字段 实例变量 和 静态变量 常量() 继承/实现 单继承(extends) 多继承(implements) 实例化 不能实例化 不能实例化 方法访问修饰符 、、 默认方法 不支持 Java8 后支持默认方法 静态方法 支持 Java8 后支持静态方法

使用场景:

  • 抽象类
    当多个类有共性行为和属性时,使用抽象类来封装这些共享代码
    当你希望有部分实现并共享实现时,使用抽象类

  • 接口
    当你希望提供某种行为的规范,不关心具体实现时,使用接口
    如果希望一个类能够继承多个行为时,使用接口(因为 Java 不支持类的多重继承,但支持多接口实现)


默认方法(default method) 是 Java 8 引入的一个新特性,允许在 接口 中提供方法的 默认实现,从而避免了接口的实现类强制必须实现所有接口方法的要求。默认方法使用 default 关键字来定义,并且可以有方法体。接口的实现类如果没有实现这个方法,默认会使用接口中提供的实现。

Q: 为什么需要默认方法?
A: 在 Java 8 之前,接口中的所有方法默认都是 抽象的,即没有方法体,必须由实现类提供具体实现。这种设计使得接口不能为已有方法提供实现,从而增加了代码的维护成本。特别是当接口已经广泛使用时,增加新的方法可能会导致大量实现类需要修改。

Q: Java8 为什么要引入默认方法,而不是直接修改 interface 支持非抽象方法
A:

  • 向后兼容性: 在 Java 8 之前,所有接口中的方法默认都是 抽象方法,实现类必须提供具体实现。如果 Java 8 改变接口的行为,允许接口中出现非抽象方法,那么现有的接口实现类(在 Java 8 发布之前编写的类)将会破坏,因为这些类可能没有实现新增的非抽象方法,导致编译错误。为了避免这种破坏性的改动,Java 8 选择引入 默认方法,即为接口的方法提供默认实现。这样,老的接口和实现类可以继续工作,而 新的接口方法可以通过默认方法提供实现,而不会破坏现有代码
  • 接口的设计哲学:接口的本质目的是 定义行为,即规定类应该具备哪些功能,但不提供具体的实现。支持非抽象方法违背了接口的设计初衷

按定义的位置来分

  1. 成员内部类,类定义在了成员位置 (类中方法外称为成员位置,无static修饰的内部类)

  1. 静态内部类,类定义在了成员位置 (类中方法外称为成员位置,有static修饰的内部类)

  1. 局部内部类,类定义在方法内

  1. 匿名内部类,没有名字的内部类,可以在方法中,也可以在类中方法外

一般用于:将匿名内部类作为参数传递




Math 类属于 包,它是一个工具类,其中的所有方法和字段都是静态的(static)。Math 类不能被实例化,因此所有的操作都是通过类名直接调用,如
默认导入,所以直接使用




同 Math 类,System 也是通过类名直接调用





Object 是所有类的超类,即所有的类都是 Object 的子类



Object 的默认实现


即 类的完全限定名@对象的哈希码的十六进制表示


示例


通常 override 重写 toString:



Object 的默认实现,比较的是对象的 地址




Object 的默认实现,只有实现了接口的类才可以支持克隆


Cloneable是一个标记接口(marker interface),这意味着它不包含任何方法,而是通过接口的存在来标识类的特性或行为


浅拷贝示例,需要重写方法


这里用了

包(需要导包)








JDK 1.0


示例



JDK 1.1

日历类是一个抽象类,不能创建对象


使用其子类 ,Gregorian 公历


两种使用方式:

  1. 直接构造

  1. Calendar 的 静态方法
    这是 静态工厂方法(Static Factory Method) 是一种通过静态方法返回类的实例的 设计模式。与通过构造方法直接创建对象相比,静态工厂方法的最大优势是可以控制返回的对象类型

常用方法

方法名 说明 public static Calendar getInstance() 获取一个它的子类 GregorianCalendar 对象 public int get(int field) 获取某个字段的值。field 参数表示获取哪个字段的值,
可以使用 Calender 中定义的常量来表示:
Calendar.YEAR : 年
Calendar.MONTH :月
Calendar.DAY_OF_MONTH:月中的日期
Calendar.HOUR:小时
Calendar.MINUTE:分钟
Calendar.SECOND:秒
Calendar.DAY_OF_WEEK:星期 public void set(int field, int value) 设置某个字段的值 public void add(int field, int amount) 为某个字段增加/减少指定的值

示例:



JDK 8

JDK8时间类类名 作用 ZoneId 时区 Instant 时间戳 ZoneDateTime 带时区的时间 DateTimeFormatter 用于时间的格式化和解析 LocalDate 年、月、日 LocalTime 时、分、秒 LocalDateTime 年、月、日、时、分、秒 Duration 时间间隔(秒,纳,秒) Period 时间间隔(年,月,日) ChronoUnit 时间间隔(所有单位)

表示时间线上的一个瞬间,它是一个基于 UTC 时区的时间点
UTC(Coordinated Universal Time, 协调世界时),如 2024-12-05T07:22:58.138799700Z



将 日期时间对象 转换为 字符串,或者将 字符串 解析为 日期时间对象



有重载方法,默认 是 ,这样就变成 2024-12-05 15:42:10 周四 下午







LocalDateTime = LocalDate + LocalTime

用于表示两个时间点之间的时间差



用于表示两个日期之间的日期差



基本类型 对应的包装类(位于java.lang包中) byte Byte short Short int Integer long Long float Float double Double char Character boolean Boolean

以 Integer 类为例

方法名 说明 public static Integer valueOf(int i) 返回表示指定的 int 值的 Integer 实例 public static Integer valueOf(String s) 返回保存指定 String 值的 Integer 对象 static string tobinarystring(int i) 得到二进制 static string tooctalstring(int i) 得到八进制 static string toHexstring(int i) 得到十六进制 static int parseInt(string s) 将字符串类型的整数转成 int 类型的整数

Q: 数组和集合的区别

  • 相同点
    都是容器,可以存储多个数据
  • 不同点
    数组的长度是不可变的,集合的长度是可变的
    数组可以存基本数据类型和引用数据类型
    集合只能存引用数据类型,如果要存基本数据类型,需要存对应的包装类

Collection 方法

方法名 说明 boolean add(E e) 添加元素 boolean remove(Object o) 从集合中移除指定的元素 boolean removeIf(Object o) 根据条件进行移除 void clear() 清空集合中的元素 boolean contains(Object o) 判断集合中是否存在指定的元素 boolean isEmpty() 判断集合是否为空 int size() 集合的长度,也就是集合中元素的个数

集合框架中的接口


集合框架中具体集合

集合类型 描述 ArrayList 可以动态增长和缩减的一个索引序列 LinkedList 可以在任何位置高效插入和删除的一个有序序列 ArrayDeque 实现为循环数组的一个双端队列 HashSet 没有重复元素的一个无序集合 TreeSet 一个有序集合 EnumSet 一个包含枚举类型值的集合 LinkedHashSet 一个可以记住元素插入次序的集合 PriorityQueue 允许高效删除最小元素的一个集合 HashMap 存储键 / 值关联的一个数据结构 TreeMap 键有序的一个映射 EnumMap 键属于枚举类型的一个映射 LinkedHashMap 可以记住键 / 值插入次序的一个映射 WeakHashMap 值不会在别处引用时就可以被垃圾回收的一个映射 IdentityHashMap 用 而不是用 比较键的一个映射

通过 获得集合的遍历器
Iterator 接口源码


示例



只有实现了 Iterable 接口的类才可以使用 迭代器 和 增强 for 循环


注:迭代器遍历时,不能用集合的方法进行增加或者删除



public interface List<E> extends SequencedCollection<E>

  • 有存取顺序的集合
  • 用户可以精确控制列表中每个元素的插入位置, 用户可以通过整数索引访问元素,并搜索列表中的元素
  • 允许重复的元素

List 特有方法


Q: 如果 是删除值为 1 的元素还是删除索引为 1 的元素?
A:ArrayList 的 remove 方法有两个重载版本:


如果 ,是删除索引为 1 的元素;如果 则删除的是值为 1 的元素


List 实现类:


  • 底层数据结构是 数组,查询快、增删慢
    时间复杂度:查询O(1),插入/删除O(n)

  • 底层数据结构是 链表,查询慢、增删快
    时间复杂度:查询O(n),头插/尾插O(1),中间O(n)

LinkedList 特有方法

方法名 说明 public void addFirst(E e) 在该列表开头插入指定的元素 public void addLast(E e) 将指定的元素追加到此列表的末尾 public E getFirst() 返回此列表中的第一个元素 public E getLast() 返回此列表中的最后一个元素 public E removeFirst() 从此列表中删除并返回第一个元素 public E removeLast() 从此列表中删除并返回最后一个元素

public interface Set<E> extends Collection<E>

  • 不可以存储重复元素
  • 没有索引,不能使用普通 for 循环遍历

Set 的实现类:


  • 可以将元素按照规则进行排序 --> 元素有序
    • :根据其元素的自然排序进行排序
    • :根据指定的比较器进行排序

底层数据结构:红黑树(自平衡二叉搜索树)
O(log n) 的时间复杂度来完成添加、删除和查找操作

注:Set 不能存储 重复 元素,各个实现类对于 重复 的定义不同:
TreeSet:根据实现了 Comparable 接口的类的重写方法 compareTo 比较的字段相同,则为重复
HashSet:判断重复时依赖 equals 和 hashCode


两种比较方法:

  1. 实现 Comparable 接口,重写 compareTo(T o) 方法 (上述是 1)
  2. TreeSet 构造方法,传入自定义的比较器(下述是2)


  • 底层数据结构是 哈希表(HashMap)
  • 无序性:HashSet 中的元素没有特定的顺序,它们的存储顺序可能与插入顺序不同。元素的顺序由哈希值决定
  • 不允许重复:HashSet 继承自 Set 接口,不允许存储重复的元素(元素是否重复由 equals 和 hashCode 方法决定
  • 没有索引, 不能使用普通for循环遍历
  • 时间复杂度:O(1) 进行添加、删除和查找操作

哈希值:根据对象的地址或者字符串或者数字算出来的int类型的数值

public interface Map<K, V>

用于存储 键值对(Key-Value)。Map 中的每个键都是唯一的,键用于映射到对应的值。Map 接口提供了一种通过键快速访问值的机制,类似于字典

方法

方法名 说明 V put(K key,V value) 添加元素 V remove(Object key) 根据键删除键值对元素 void clear() 移除所有的键值对元素 boolean containsKey(Object key) 判断集合是否包含指定的键 boolean containsValue(Object value) 判断集合是否包含指定的值 boolean isEmpty() 判断集合是否为空 int size() 集合的长度,也就是集合中键值对的个数 V get(Object key) 根据键获取值 Set<K> keySet() 获取所有键的集合 Collection<V> values() 获取所有值的集合 Set<Map.Entry<K,V>> entrySet() 获取所有键值对对象的集合

遍历:

  1. forEach lambda 直接遍历 map,不能修改值
  2. ,可以通过 修改值

Map 的实现类:

  • HashMap
  • TreeMap
  • 底层数据结构:哈希表
  • 依赖 hashCode 方法和 equals 方法保证键的唯一
  • 如果键要存储的是自定义对象,需要重写 hashCode 和 equals 方法


  • 底层数据结构:红黑树
  • 依赖自然排序或者比较器排序, 对键进行排序
  • 如果键存储的是自定义对象,需要实现Comparable接口或者在创建TreeMap对象时候给出比较器排序规则

是集合工具类,用来对集合进行操作

常用方法


  • 自然排序


  • 自定义排序


在 JDK 9 之后,方法返回的是一个不可变集合
不可变集合的好处:

  1. 性能更好
    不可变集合在某些场景下(比如并发访问时)具有更好的性能,因为不需要额外的同步机制来保证线程安全。
    它也可以减少一些内存消耗,因为不可变对象可以共享,避免了对同一数据的多次修改
  2. 更安全
    返回不可变集合是一种保护数据不被外部修改的设计方式,可以让开发者放心地传递集合数据而不必担心被修改。


方法可以接受多个相同类型的参数,而无需显式地指定每个参数

其中 elements 是一个 T 泛型数组

示例



Stream 流 是一个数据元素的序列,可以支持顺序和并行的聚合操作。Stream 本身 不存储数据,它是对数据源的一个 视图

中间操作会将一个流转换为另一个流,但它是 懒加载 的(只有在执行终端操作时才会真正执行)



筛选符合条件的元素。filter 方法接收一个 Predicate 函数式接口,只有当 Predicate 返回 true 时,元素才会被保留在结果流中。


函数式接口可以使用 lambda 表达式简化:



传入一个函数式接口 Function 来对元素进行处理,并返回一个包含转换后元素的新流



终端操作触发流的执行,并返回一个结果



工具类 Collectors 提供了具体的收集方式

方法名 说明 public static <T> Collector toList() 把元素收集到List集合中 public static <T> Collector toSet() 把元素收集到Set集合中 public static Collector toMap(Function keyMapper,Function valueMapper) 把元素收集到Map集合中

将流中的元素合并为单一结果,比如求和、求积、连接字符串等





异常(Exception) 是程序运行中出现的一种错误事件,它会中断正常的程序执行流程。Java 提供了一个强大的异常处理机制,可以捕获和处理这些异常,避免程序因异常而崩溃
根类 有两个子类 和

Throwable 的常用方法


异常(Exception)的分类 :

  1. 检查异常(Checked Exception)-> 编译时,如时间类的formatter
  2. 运行时异常(Runtime Exception)-> 运行时,如数组下标越界

举例:


当程序执行到 时,由于访问数组索引超出范围,Java 会检测到这个运行时错误并创建一个 ArrayIndexOutOfBoundsException 异常对象

然后,这个异常对象会被抛出到当前方法(main 方法)的调用栈中。如果 main 方法中没有显式的异常处理逻辑(比如 try-catch 块),异常会继续向上抛给调用者

对于 main 方法而言,它的调用者是 JVM。当 JVM 收到异常时,会捕获这个异常,打印异常的堆栈信息(包含异常的类型、详细信息和抛出异常的代码位置),然后终止程序的执行

throw 用在方法内,用来抛出一个异常对象,将这个异常对象 传递到调用者 处,并 结束 当前方法的执行



在 7.1 调用者收到了异常,需要进行处理,两种方法:

  • 声明异常
  • 捕获异常

声明异常:将问题标识出来,报告给调用者。如果方法内通过 throw 抛出了 编译时异常,而没有捕获处理(稍后讲解该方式),那么必须通过throws进行声明,让调用者去处理

关键字 throws 运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常(抛出异常)

以读文件为例,需要判断文件是否存在,这是编译时的异常
下列代码编译器报错:未处理异常: java.io.FileNotFoundException


对于检查时异常,需要有处理的方法,这里使用 声明异常,即 向上抛给调用者


在 main() 方法中调用 readTxt() 方法,也需要声明异常,继续往上抛给 JVM



如果异常出现的话,会立刻终止程序,所以需要 处理异常

Java 中对异常有针对性的语句进行捕获,可以对出现的异常进行指定方式的处理


捕获数据下标越界异常,如果超过了就返回边界



获取异常信息:
Throwable 类中定义了一些查看方法



对于多个异常:



有一些特定的代码无论异常是否发生,都需要执行。另外,因为异常会引发程序跳转,导致有些语句执行不到。而finally就是解决这个问题的,在finally代码块中存放的代码都是一定会被执行的。

什么时候的代码必须最终执行?

当我们在try语句块中打开了一些物理资源(磁盘文件/网络连接/数据库连接等),我们都得在使用完之后,最终关闭打开的资源

用于 回收资源

在 catch 后面

bit(比特):信息的最小单位,代表一个二进制位,即 0 或 1
byte(字节):计算机存储的基本单位,1 byte = 8 bit

根据数据的流向分为

  • 输入流 :把数据从 其他设备 上读取到 内存 中的流
  • 输出流 :把数据从 内存 中写出到 其他设备 上的流

根据数据的类型分为

  • 字节流 :以字节为单位->
  • 字符流 :以字符为单位,如 Unicode 使用多个字节来表示->
输入流 输出流 字节流 字节输入流
InputStream 字节输出流
OutputStream 字符流 字符输入流
Reader 字符输出流
Writer

注:现在的"流" 和 java.util.streamd 流 不同

抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法:



以下是 OutputStream 常见子类:

  • 用于将字节写入文件

文件输出流,用于将数据写出到文件


举例:


注:对于方法 输入的 b 如果大于了1字节,会被截断,只会保留一个字节的信息写出

抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法



文件输入流,从文件中读取字节


示例







当使用字节流读取文本文件时,可能会有一个小问题。就是遇到中文字符时,可能不会显示完整的字符,那是因为一个中文字符可能占用多个字节存储。所以Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件









try-with-resources 是 Java 7 引入的一种语法,它简化了对资源(如文件、数据库连接、网络连接等)的管理。通过这种语法,Java 会自动关闭在 try 块中打开的资源,确保资源被正确释放,而无需显式调用 close() 方法


不需要手动 close() 关闭

提高输入输出效率,通过在内存中设置一个缓冲区来减少对物理设备(如文件或网络)的频繁访问,是对 FileInputStream、FileOutputStream、FileReader、FileWriter 的增强

原理
缓冲流通过将数据从物理设备读取到内存缓冲区中,或者将数据从缓冲区写入物理设备,来 减少磁盘 I/O 操作的频率。这样,数据的读写操作并不是每次都直接进行,而是先在内存中缓存一定量的数据,再批量进行读写。(内存访问速度远快于磁盘)


示例







序列化(Serialization)是指将 Java 对象转换为字节流的过程,以便于存储在文件中、通过网络传输或将对象保存到数据库等。反序列化(Deserialization)是将字节流转换回 Java 对象的过程

序列化通常用于以下几种场景:

  • 持久化:将对象状态保存到磁盘或数据库中,以便在未来恢复
  • 网络通信:通过网络发送对象,使得远程调用更加容易
  • 深拷贝:通过序列化和反序列化实现对象的深拷贝

一个对象想要序列化需要实现序列化,需要实现 标记接口
该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用 关键字修饰

将 Java 对象的写出到文件



将 ObjectOutputStream 序列化的原始数据恢复为 Java 对象


示例,保存 student 列表



前情提要:
关于
是 包下的一个类,所以默认导包, 和 分别是其 输入流 和 打印流 的 静态常量 对象




多线程:允许在一个程序中同时执行多个任务,从而提高程序的效率

并行 与 并发:

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

进程 和 线程

  • 进程:正在运行的程序
  • 线程:是程序执行的最小单位。每个线程都有自己的执行路径,可以并发地执行代码

单线程:一个进程如果只有一条执行路径,则称为单线程程序
多线程:一个进程如果有多条执行路径,则称为多线程程序

实现多线程的两种方法:

  1. 继承 Thread 类
  2. 实现 Runnable 接口

继承 类并重写 方法来定义线程的执行逻辑
然后用 方法启动线程

Thread 方法

  • run() 封装线程执行的代码,直接调用,相当于普通方法的调用
  • start() 启动线程;然后由 JVM 调用此线程的 run() 方法
  • join() 当主线程调用一个线程对象的 join() 方法时,主线程会进入阻塞状态,直到被调用的线程执行完毕后,主线程才会继续执行


注:


Thread 实际上实现了 Runnable 接口
而 Runnable 接口来自于 包

定义一个 类实现 接口,并重写 方法
创建 类的对象,把 对象作为构造方法的参数



接口来自于 包







线程调度 的两种调度方式

  • 分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片
  • 抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些

Java 使用的是 抢占式调度模型



某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票


部分结果截图如下,剩余的票数是乱的,因为三个线程的优先级的相同的(可以通过 方法获得优先级,默认是 5),所以三个线程竞争 cpu 的使用权,cpu 随机选线程执行 =====> 数据安全问题

数据安全问题的原因:

  • 是多线程环境
  • 有共享数据
  • 有多条语句操作共享数据

解决数据安全问题 =====> 线程同步
是 Java 提供的基本同步机制,它可以确保在同一时刻只有一个线程能够访问某个代码块或方法,从而避免数据冲突。

  • 优点:解决了多线程的数据安全问题
  • 缺点:当线程很多时,因为每个线程都会去判断同步上的锁,耗费资源,降低程序的运行效率

两种 使用方法:

  1. 同步代码块
  2. 同步方法





死锁是指两个或多个线程在执行过程中由于竞争资源而相互等待,从而导致线程无法继续执行的状态

死锁产生的条件
根据经典的死锁理论,死锁的产生必须满足以下 4 个条件,一旦打破其中一个条件,就可以避免死锁:

  • 互斥条件
    线程需要独占资源,其他线程无法同时访问该资源
  • 占有并等待
    线程已经持有一个资源,同时等待另一个资源
  • 不可剥夺
    线程持有的资源在未完成任务前,不能被其他线程强制剥夺
  • 循环等待
    两个或多个线程形成一个资源等待环,线程 A 等待线程 B 占用的资源,线程 B 等待线程 A 占用的资源,以此类推

Thread 1: Holding lock1...
Thread 2: Holding lock2...
Thread 1: Waiting for lock2...
Thread 2: Waiting for lock1...
程序会卡在最后两个打印消息,线程 1 和线程 2 互相等待,导致死锁

多个生产者线程向一个共享缓冲区中生产数据,多个消费者线程从缓冲区中消费数据

共享缓冲区

  • 缓冲区有固定容量
  • 生产者线程向缓冲区中放入数据,消费者线程从缓冲区中取出数据

同步要求

  • 生产者必须在缓冲区满时等待(不能再放入数据)
  • 消费者必须在缓冲区空时等待(没有数据可以取)

线程安全

  • 多线程访问共享缓冲区时需要保证线程安全,避免竞态条件

实现方法:

  1. 方法和 方法
  2. 阻塞队列

示例



阻塞队列(BlockingQueue)是 Java 提供的一种 线程安全 的数据结构,广泛用于生产者消费者模型

它的特点是:

  • 线程安全
    通过内置的锁机制确保线程间的数据访问安全。
  • 自动阻塞
    在队列为空时,消费者线程会阻塞等待;在队列满时,生产者线程会阻塞等待

内置线程安全机制,无需显式编写 wait() 和 notify()和 synchronized

最新文章
如何制定有效的CDN应急预案解决方案?
CDN应急预案解决方案1、监控系统的建立:构建全面、实时的监控系统,覆盖源站状态、节点健康、网络延迟和带宽利用率等关键指标,通过实时监控及时发现异常情况,为后续故障排查提供线索。2、日志分析与追踪:利用日志分析工具对海量日志文
郑州发布:中小学编程培训学校十大排名揭秘一览
少儿编程符合当前的教育*趋势,培养孩子的编程技能是时代的要求。孩子学习儿童编程可以开发大脑,提升专注力。下面小编给大家介绍一下国内排名靠前的少儿编程培训机构。1.童程童美少儿编程2.西瓜创客少儿编程3.昂立编程4.编完边学5.中公趣码
连云港爱采购免费入驻
百度爱采购的服务以及做到好的转化效果有哪些操作?百度终究是个搜索引擎,做的越早以后排名越好,而且随着时间推移越来越稳定;现在百度爱采购,关键词是可以无限设置的,产品不受数量限制,趁着百度没限制,入驻很有必要,此后门槛可能会
【长沙】AITO问界大型团购会【2024-12-19 星期四】
本《卡盟网团购服务使用协议》隶属于好多车信息技术(北京)有限公司旗下《用户使用协议》的重要组成部分,若您欲访问和使用卡盟网团购频道,接受本网站提供的服务,您应当仔细阅读本协议的全部内容,尤其是涉及您重大权益的加粗部分的文字
微信小程序框架选哪个?商城搭建指南
微信小程序框架选哪个?商城搭建指南微信小程序框架是小程序开发必不可少的工具。然而,市场上的框架有很多种,如何选择最适合自己项目需求的框架?Taro框架是一款基于React语法的多端开发框架,可以同时开发小程序、H5和APP等多个平台。它
SEO优化中的网站结构设计
网站结构设计在SEO优化中的重要性网站结构设计是搜索引擎优化(SEO)中的一个关键要素,它不仅影响用户体验,还直接关系到网站在搜索引擎中的排名。一个合理的网站结构可以帮助搜索引擎更好地抓取和索引网站内容,从而提升网站的可见性。本
央行承认的POS机品牌,安全可靠,值得信赖
央行认可的POS机品牌是安全可靠的,值得信赖。这些品牌的POS机具有先进的技术和管理团队,能够保障交易的安全性和稳定性。央行对于POS机的审核非常严格,只有通过审核的品牌才能被认可。使用央行认可的POS机可以有效地降低交易风险,提高交
Wordpress外贸建站 | 最全面的Wordpress外贸网站教程(附视频讲解)
郑州白帽子是专业做外贸网站定制、谷歌SEO网站内容的公司,希望通过这篇外贸建站教程可以让你对外贸建站有一个系统的认知。如果你还不知道做外贸的全流程,可以看下新手如何做外贸这篇教程。为什么要把外贸网站定位放在第一步? 因为定位是
知乎文章关键词搜索排名置顶第一的方法!
知乎作为一家用户流量巨大的问答类社群平台,引流能力自然不言而喻,然而拥有巨大的流量入口就有超乎寻常的竞争。那我们如何才能将在知乎发布的文章,在用户搜索关键词的时候,让文章的排名靠前或置顶呢?笔者今天给大家讲解下知乎关键词排
Shell学习与Hadoop进阶
1.shell学习笔记 如下 2.明日学习计划         下一步是学习Hadoop,之前看过800分钟的视频,学会了搭建Hadoop,hive,并连接帆软BI进行展示。今天看了另一个系列的视频,有1600多分钟,其中
相关文章
推荐文章
发表评论
0评