欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 手游 > java基础3.0

java基础3.0

2025/6/8 14:25:04 来源:https://blog.csdn.net/C18298182575/article/details/144892654  浏览:    关键词:java基础3.0

 sleep() 和 yield() 的区别:

Thread.sleep() 和 Thread.yield() 都是 Java 中用于控制线程执行的两种方法,但它们的行为和用途有所不同。

1. Thread.sleep(long millis)

Thread.sleep() 方法使当前正在执行的线程 暂停指定的时间,不会占用 CPU 资源。它是静态方法,接受一个参数 millis(表示暂停的时间,以毫秒为单位)。可以通过 sleep() 方法来控制线程的执行节奏。

  • 作用:使当前线程进入 休眠状态,暂停执行指定的时间(单位是毫秒)。

  • 不会释放锁:如果当前线程持有某个锁(如同步块中的锁),它在 sleep() 期间仍然保持对该锁的持有。

  • 不能被中断sleep() 方法可能会抛出 InterruptedException 异常,因此通常需要进行异常处理。

  • 例子

    try { Thread.sleep(1000); // 当前线程暂停执行1秒 } catch (InterruptedException e) { e.printStackTrace(); }

  • 用途:当我们想让当前线程暂停一段时间后再继续执行时,使用 sleep()

2. Thread.yield()

Thread.yield() 方法提示当前正在执行的线程 放弃 CPU 使用权,使得其他线程有机会执行,但并不保证当前线程会立即停止。它是一个 建议性 方法,告诉线程调度器当前线程愿意让出 CPU 给其他线程,优先级相同的情况下,其他线程有可能被调度执行。

  • 作用:使当前线程 自愿让出 CPU 时间片,并不意味着线程会暂停,调度器可能会选择该线程继续执行,或者调度其他同优先级的线程。

  • 释放锁yield() 不会释放锁,持有的锁仍然会被当前线程保留。

  • 无法保证立即切换yield() 是一种提示,操作系统的线程调度器可以选择不立即让当前线程让出 CPU,因此线程可能会继续执行。

  • 例子

    Thread.yield(); // 当前线程让出 CPU

  • 用途yield() 主要用于帮助多线程程序达到某种调度上的公平性,或者在多线程环境下给其他线程执行的机会,尤其是在没有同步机制时。

sleep() 和 yield() 的区别:

特性sleep()yield()
线程状态使线程进入 休眠状态使线程进入 就绪状态,但不暂停
是否释放 CPU释放 CPU 资源,暂停当前线程让出 CPU 资源,但线程可能继续执行
是否让出锁不会释放锁不会释放锁
是否可被中断可以被中断不可被中断
调度器行为调度器按照 sleep 期间指定的时间休眠仅是提示,不保证线程会立即停止
典型用途用于让线程暂停执行一定的时间用于让出 CPU,给予其他线程执行的机会

java中值传递还是引用传递

Java 中的参数传递是 值传递,但理解这一点时需要注意区分 基本数据类型 和 引用数据类型 两种情况。

1. 基本数据类型(如 intcharfloat 等):

当你将一个基本数据类型的变量作为参数传递给方法时,传递的是该值的副本。方法内对该副本的修改不会影响原始变量的值。

public class Test {public static void main(String[] args) {int a = 10;modifyValue(a);System.out.println(a);  // 输出 10}public static void modifyValue(int a) {a = 20;}
}

在上面的例子中,a 的值在 modifyValue 方法中被修改为 20,但因为是值传递,main 方法中的 a 依然保持为 10

2. 引用数据类型(如对象、数组等):

当你将一个引用数据类型的变量作为参数传递时,传递的是该对象的 引用 的副本。这个副本指向与原始引用相同的对象。因此,在方法中对对象的修改会影响原始对象。但如果修改引用本身(例如,指向不同的对象),则不会影响原始引用变量。

public class Test {public static void main(String[] args) {Person p = new Person("John");modifyPerson(p);System.out.println(p.name);  // 输出 "Jane"}public static void modifyPerson(Person p) {p.name = "Jane";  // 修改对象的属性}
}class Person {String name;Person(String name) {this.name = name;}
}

在这个例子中,p 是一个引用类型变量,传递给 modifyPerson 方法时,传递的是对象的引用副本。因此,在方法中修改 p.name 会影响 main 方法中的对象。

但是,如果在方法中重新赋值给 p,它会指向一个新的对象,而不会影响 main 中的 p 引用:

public static void modifyPerson(Person p) {p = new Person("Alice");  // 重新赋值,p 指向了新的对象
}

// 重新赋值,p 指向了新的对象 }

这段代码中的 p 只会影响方法中的局部变量 p,原始的 p 引用(在 main 中)不会受到影响。

synchronized

my:对象 对象头(Mark Word(锁状态)),监视器锁 锁标识位(如无锁、偏向锁、轻量级锁、重量级锁等),锁状态,CAS

synchronized 是 Java 中一种用于保证线程安全的机制,它通过加锁的方式来控制对共享资源的访问。它的实现原理基于 监视器锁(Monitor Lock)和 对象头,通过加锁和解锁来确保同一时刻只有一个线程可以执行被 synchronized 修饰的代码块或方法。

synchronized 实现原理

  1. 对象头和监视器锁

    • 每个 Java 对象都有一个与之关联的对象头,Java 虚拟机将对象头分为两部分:Mark Word 和 Klass Pointer。Mark Word 存储着对象的哈希码、GC信息、锁状态等。
    • 在 Java 中,synchronized 锁是通过对象的监视器(monitor)来实现的。每个对象都对应一个监视器,监视器的管理由 JVM 提供。通过监视器来控制线程的访问,保证在同一时刻只有一个线程能访问同步代码块或方法。
  2. 锁的获取与释放

    • 当一个线程访问被 synchronized 修饰的方法或代码块时,首先需要获得对象的锁。若其他线程已经持有该锁,当前线程将进入阻塞状态,直到锁被释放。
    • 线程获取锁时,会通过修改对象头中的锁标志位来标记该对象的状态。JVM 使用对象头中的标志位来管理不同的锁状态(如无锁、偏向锁、轻量级锁、重量级锁等)。
  3. 锁的升级与降级

    • 偏向锁:线程首次获取锁时,JVM 会首先尝试为该锁分配偏向锁,偏向锁会将锁的标志指向当前线程 ID,表示当前线程对该对象拥有锁的偏向。如果线程后续继续访问该对象,则无需再竞争锁。
    • 轻量级锁:如果锁已经被其他线程持有,JVM 会尝试通过原子操作(CAS)来获取轻量级锁。轻量级锁通过在对象头中使用 CAS 操作来尝试获取锁,不会阻塞当前线程。轻量级锁是为了减少线程间的竞争,提高性能。
    • 重量级锁:当多线程竞争轻量级锁失败,或者轻量级锁升级失败时,JVM 会将锁升级为重量级锁。重量级锁涉及操作系统的原子操作,线程会被阻塞,直到获取锁为止。
  4. 内存可见性保证

    • synchronized 还确保了内存的可见性。当一个线程释放锁时,所有它所做的修改会被刷新到主内存中,其他线程在获得该锁时,能看到这些修改。这样就保证了共享数据的可见性和一致性。
    • 同时,synchronized 还会保证在加锁区域内,所有操作的原子性,避免了多个线程同时修改共享数据的问题。
  5. JVM中的锁优化

    • JVM 会根据线程的竞争情况动态地调整锁的实现。例如,synchronized 使用时,JVM 会使用 自旋锁 来避免频繁的线程阻塞/唤醒操作,减少线程切换的开销。
    • 如果某个线程获取锁后执行时间很短,JVM 会尝试通过自旋来减少线程的上下文切换,降低性能损耗。

锁的不同状态:

synchronized 实现的锁具有不同的状态,用来表示锁的竞争情况:

  1. 无锁状态:对象没有任何线程持有锁,可以被任何线程获取。
  2. 偏向锁:一个线程持有锁并且没有其他线程竞争时,锁会变为偏向锁。偏向锁的目的是提高性能,减少锁的竞争。
  3. 轻量级锁:多个线程竞争时,JVM 会通过 CAS(Compare-And-Swap)操作来尝试获取锁,但不会阻塞线程。轻量级锁的目的是减少线程间的竞争,提高性能。
  4. 重量级锁:当多个线程竞争激烈时,JVM 会将锁提升为重量级锁。此时,线程会进入阻塞状态,直到获取锁。重量级锁涉及操作系统的调度和线程上下文切换,性能较差。

并发与并行

并发(Concurrency)和并行(Parallelism)是计算机科学中常常被混淆的两个概念,虽然它们之间有些相似,但在本质上存在区别。

1. 并发(Concurrency)

并发是指多个任务在同一时间段内被处理,但不一定是同时执行的。并发的核心是处理多个任务的能力,无论这些任务是按顺序执行,还是交替执行。换句话说,并发是让计算机在多个任务之间快速切换,目的是提高系统的响应能力。

  • 并发通常发生在单核或多核处理器中,其中任务是交替执行的。即使是一个单核处理器,它也能通过快速切换任务,使得用户感受到“并发”的效果。
  • 在并发系统中,操作系统通过调度算法管理线程的执行,使得多个任务看起来像是同时在执行,尽管它们实际上是依次执行的。

并发的关键点:

  • 任务可以在同一时间段内执行,但并不需要真正同时执行。
  • 通过切换上下文(如线程调度)来实现并发。
  • 并发不一定要求多核处理器。

2. 并行(Parallelism)

并行是指多个任务在同一时间真正同时执行。并行的核心是在多个处理器核心(CPU cores)上同时执行多个任务,以提高计算效率。

  • 并行通常依赖于多核处理器或多个处理器,在每个核心上同时执行不同的任务。每个任务或其部分在不同的处理器或核心上并行执行,从而实现加速。
  • 并行是一种特殊的并发,它不仅仅是任务之间的切换,而是多个任务同时进行。

并行的关键点:

  • 任务真正同时执行。
  • 通常依赖多核处理器或多个处理器。
  • 适用于需要大量计算的任务,可以大幅提高处理速度。

3. 并发与并行的区别

特性并发(Concurrency)并行(Parallelism)
定义同一时间处理多个任务,任务可能交替执行多个任务在同一时刻真正并行执行
执行方式在单核或多核处理器上通过快速切换任务实现在多核处理器或多个处理器上,任务同时执行
目标提高程序的响应性和吞吐量,处理多个任务提高计算效率,减少执行时间
依赖硬件不一定需要多核处理器,可在单核处理器上实现需要多核处理器或多个处理器进行并行计算
例子操作系统调度多个程序的执行,Web服务器处理多个用户请求分布式计算,图像处理中的大规模数据并行处理

4. 简单类比

  • 并发:你有多个任务(如做饭、洗衣、擦桌子),虽然你一次只能做一件事,但你可以快速切换在这些任务之间,使得看起来你能同时做这些事情。
  • 并行:你有多个任务(如做饭、洗衣、擦桌子),你和你的朋友们每个人做一项任务,大家同时工作,这样所有任务都能同时完成。

5. 实际应用

  • 并发:操作系统调度任务,Web服务器同时处理多个用户请求。
  • 并行:科学计算、大数据处理、图像处理等需要大量计算资源的应用,通过多核处理器或分布式计算来实现并行处理。

分布式事务

7 层网络模型(OSI 模型)+三次握手

7 层网络模型(OSI 模型)

OSI 模型是一个抽象的网络通信框架,将通信任务分为七个层次,每一层都提供特定功能。


OSI 7 层结构
  1. 物理层(Physical Layer)

    • 功能:负责数据的物理传输,处理电平、光信号、接口等硬件相关问题。
    • 设备:网线、光纤、集线器、网络接口卡。
    • 传输单位:比特(Bit)。
  2. 数据链路层(Data Link Layer)

    • 功能:将比特组装成帧,提供可靠的点对点传输,处理差错检测和纠正。
    • 协议:Ethernet、PPP、HDLC。
    • 设备:交换机。
    • 传输单位:帧(Frame)。
  3. 网络层(Network Layer)

    • 功能:负责数据的路由和转发,实现不同网络间的通信。
    • 协议:IP、ICMP、IGMP。
    • 设备:路由器。
    • 传输单位:分组/数据包(Packet)。
  4. 传输层(Transport Layer)

    • 功能:提供端到端的通信,负责数据分段、重组,确保可靠性(如 TCP)或快速性(如 UDP)。
    • 协议:TCP、UDP。
    • 传输单位:段(Segment)。
  5. 会话层(Session Layer)

    • 功能:建立、管理、终止会话,处理会话的同步与恢复。
    • 协议:NetBIOS、RPC。
  6. 表示层(Presentation Layer)

    • 功能:处理数据的表示、加密、解密、压缩,保证数据格式的正确性。
    • 协议:SSL/TLS、JPEG、MPEG。
  7. 应用层(Application Layer)

    • 功能:提供用户与网络之间的接口,直接面向用户。
    • 协议:HTTP、FTP、SMTP、DNS。

TCP 为什么要三次握手?

三次握手的目的

TCP 是面向连接的协议,三次握手用于在客户端与服务器之间建立可靠的通信通道,确保双方的收发能力正常,并防止旧连接的干扰。


三次握手过程
  1. 第一次握手:客户端发送 SYN

    • 客户端向服务器发送一个 SYN(同步)包,表示希望建立连接,并请求序列号(如 seq=x)。
    • 目的:告诉服务器,客户端希望建立连接。
  2. 第二次握手:服务器回复 SYN-ACK

    • 服务器收到 SYN 包后,确认客户端的请求,回复 SYN-ACK 包,包含服务器的序列号(如 seq=y)和确认号(如 ack=x+1)。
    • 目的:告诉客户端,服务器收到连接请求,并可以进行通信。
  3. 第三次握手:客户端发送 ACK

    • 客户端收到 SYN-ACK 包后,发送 ACK 包,包含确认号(如 ack=y+1)。
    • 连接建立,双方可以开始通信。
    • 目的:确认服务器的回复,建立可靠连接。

为什么不能是两次握手?
  1. 两次握手可能导致旧连接误导
    如果网络中有延迟的旧 SYN 包,服务器收到后建立连接,而客户端早已关闭,服务器资源可能被浪费。

  2. 三次握手确保双方状态正常

    • 第一次:客户端确认自己可以发送数据。
    • 第二次:服务器确认自己可以接收并发送数据。
    • 第三次:客户端确认自己可以接收数据。

AQS

my:

CAS state 队列(线程阻塞,挂起)

公平锁/非公平锁

非公平锁:cas 

tryQquire(state / thread)是否等于0 是:cas 否:判断线程 count++

加入队列 unsafe. LockSport lock/unlock

-------

AQS(Abstract Queue Synchronizer)是 Java 提供的一种用于构建同步工具的框架,它是 java.util.concurrent 包中的一个抽象类,许多同步器(如 ReentrantLockCountDownLatchSemaphore 等)都基于 AQS 实现。AQS 提供了一个简单的机制来管理线程的获取与释放同步资源的过程。

AQS 的核心概念

AQS 主要通过维护一个 FIFO 队列(双向队列)来管理请求访问同步资源的线程。AQS 使用 共享锁 和 独占锁 的模型来对线程的访问进行控制。

1. 同步队列(Sync Queue)

AQS 内部有一个 FIFO 等待队列,用于存放正在等待资源的线程。当线程无法获得锁时,它会被加入到该队列,直到获得资源。

2. 共享锁和独占锁
  • 独占锁:一次只能有一个线程获取锁,其他线程需要等待(例如 ReentrantLock)。
  • 共享锁:多个线程可以共享锁,但仍需满足一些条件(例如 Semaphore,可以允许多个线程同时访问资源)。
3. 状态变量(State)

AQS 使用一个 状态变量 来表示同步状态。这个状态变量通常用于标识当前锁是否被占用,或资源的剩余量等。同步状态是 AQS 的核心,线程可以通过 getState() 和 setState() 方法来获取和更新状态。

  • 对于 独占锁,状态一般表示锁是否被占用,0 表示未占用,1 表示已占用。
  • 对于 共享锁,状态可以表示当前可用的资源数量,0 表示无可用资源,正数表示剩余的可用资源数量。

AQS 的核心方法

AQS 提供了若干核心方法来进行同步控制,线程通过这些方法来请求资源、释放资源或等待。常见的方法有:

  • **acquire()**:尝试获取锁。如果锁已经被其他线程占用,当前线程就会被挂起,直到获取到锁。
  • **release()**:释放锁,并唤醒等待队列中的线程。
  • **tryAcquire()**:尝试获取锁,如果获取不到就返回 false
  • **tryRelease()**:尝试释放锁。
  • **hasQueuedThreads()**:判断是否有线程在等待获取锁。
  • **addWaiter()**:将当前线程添加到等待队列中。
  • **isHeldExclusively()**:判断当前锁是否被当前线程独占。

AQS 的工作原理

  1. 线程获取锁

    • 当一个线程调用 acquire() 或类似方法请求锁时,它会先尝试更新状态(如尝试获取锁)。
    • 如果成功,线程就能独占资源。如果失败,线程就会被加入到同步队列中,并进入等待状态,直到有资源可用。
  2. 线程释放锁

    • 释放锁时,线程会通过 release() 方法更新状态并唤醒队列中的其他线程,使得其他等待的线程有机会获取资源。
  3. 队列管理

    • 等待队列中的线程根据 AQS 提供的队列管理机制,以先进先出(FIFO)的顺序来获取锁。

AQS 的应用

AQS 是许多 Java 并发工具的基础,它简化了同步器的实现。例如:

  • **ReentrantLock**:通过 AQS 提供的独占锁机制实现了可重入锁的功能。
  • **CountDownLatch**:基于 AQS 的共享锁机制实现了线程间的计数器,允许一个线程等待其他线程执行完毕。
  • **Semaphore**:通过 AQS 提供的共享锁机制实现了一个限制并发的信号量。
  • **CyclicBarrier**:通过 AQS 实现的一个可以循环使用的屏障,允许一组线程互相等待,直到达到某个条件。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词