基础理解-线程与锁(Base-Thread and Lock)

本文介绍了Java应用程序中的线程和锁的基本概念。每个Java进程至少有一个应用线程和一些内部线程,如垃圾回收线程。线程的调度由操作系统处理,Java线程通过对象表示,具有固定大小的栈,超出栈大小会引发栈溢出错误。锁用于同步共享数据,防止错误,Java中有多种锁类型,包括fat lock、thin lock、recursive lock和lazy lock。自旋和休眠是两种等待锁的方式,而锁链则描述了线程间锁的依赖关系。了解这些有助于优化应用和JVM性能。

[2023.02.22]

From Url:

原文链接:

Understanding Threads and Locks

Understanding Threads and Locks

理解线程和锁

A running application is usually made up of one process with its own memory space. A computer is generally running several processes at the same time. For example, a word processor application process might be running alongside a media player application process. Furthermore, a process consists of many concurrently running threads. When you run a Java application, a new JVM process is started.

一个运行的应用通常由若干个拥有自己内存空间的进程构成。一台设备一般会有多个进程同时运行。例如:文字处理应用的进程可以和播放器进程同时运行。而且,一个进程中会包含多个正在运行的线程。当你在运行一个Java应用时,一个对应的JVM(Java虚拟机)会被启动。

Each Java process has at least one application thread. Besides the threads of the running Java application, there are also Oracle JRockit JVM internal threads that take care of garbage collection or code generation.

每一个Java进程中都最少存在一个应用线程,除此之外,还会有垃圾回收和代码生成的 Oracle JRockit JVM内部线程存在。

This section contains basic information about threads and locks in the JRockit JVM. The following subjects are discussed:

这个章节介绍了 Oracle JRockit JVM线程和锁的一些基础信息。

For information about how to make so-called thread dumps, printouts of the stacks of all the threads in an application, see Using Thread Dumps. Thread dumps can be used to diagnose problems and optimize application and JVM performance.

对于打印线程的堆栈信息(thread dumps)的信息,请参阅 Using Thread Dumps 。线程的堆栈信息在 “排查问题” 和 “优化应用和虚拟机性能” 时起到作用。

Understanding Threads

理解线程

A java application consists of one or more threads that run Java code. The entire JVM process consists of the Java threads and some JVM internal threads, for example one or more garbage collection threads, a code optimizer thread and one or more finalizer threads.

一个java应用包含一个或多个执行java代码的线程。在整个JVM进程中包含Java线程和一些JVM内部的线程,JVM内部线程诸如:一个或多个垃圾回收线程、代码优化线程、一个或多个finalizer线程。

From the operating system’s point of view the Java threads are just like any application threads. Scheduling of the threads is handled by the operating system, as well as thread priorities.

从操作系统的角度看,Java线程与任何应用中的线程一样,被操作系统根据优先级调度。

Within Java, the Java threads are represented by thread objects. Each thread also has a stack, used for storing runtime data. The thread stack has a specific size. If a thread tries to store more items on the stack than the stack size allows, the thread will throw a stack overflow error.

在Java中,Java线程表现为一个线程对象,每一个线程都有自己的栈来存放运行时数据。这个栈有一定大小,如果超过了栈的容量,那么线程会抛出一个栈溢出的错误。

Default Stack Size for Java Threads

Java线程的默认大小

This section lists the default stack sizes. You can change the thread stack size with the -Xss command line option, for example:

下表列出了默认的栈大小。可以通过 -Xss命令来修改栈的大小,举例如下:

java -Xss:512k MyApplication

The default stack sizes differ depending upon whether you are using IA32 and X64, as shown in Table 1:

栈的默认大小取决于具体使用的是IA32或者X64系统,细节在表1(Table 1)中。

Table 1 Default Stack Size

OS

Default Stack Size

Windows IA32

64 kB

Windows IA64

320 KB

Windows x64

128 kB

Linux IA32

128 kB

Linux IA64

1024 KB

Linux x64

256 kB

Solaris/SPARC

512 KB

Default Stack Size for JVM Internal Threads

JVM内部线程默认栈大小

A special “system” stack size is used for JVM internal threads; for example, the garbage collection and code generation threads. The default system stack size is 256 KB on all platforms.

特殊的“系统”栈大小数据是用在如垃圾回收或代码生成这样的JVM内部线程上的。默认大小在全平台上是256KB。

Note:The -Xss command line option sets the stack size of both application threads and JVM internal threads.

Understanding Locks

理解锁

When threads in a process share and update the same data, their activities must be synchronized to avoid errors. In Java, this is done with the synchronized keyword, or with wait and notify. Synchronization is achieved by the use of locks, each of which is associated with an object by the JVM. For a thread to work on an object, it must have control over the lock associated with it, it must “hold” the lock. Only one thread can hold a lock at a time. If a thread tries to take a lock that is already held by another thread, then it must wait until the lock is released. When this happens, there is so called “contention” for the lock.

当同一进程中的不同线程共同使用和修改同一数据时,为了避免发生错误,他们的行为必须被同步处理。在Java中,可以通过synchronized关键字或者对象的wait和notify方法来实现。同步的实现是通过对象的锁来完成的。一个线程必须拥有这个对象的锁,并且持有这个锁才能正常地操作这个对象,而每个锁在同一时间内只能被一个线程持有。如果其他线程期(线程B)望获取某个已经被持有的锁(线程A持有),那么这个线程(线程B)只能等待(线程A)释放这个锁,这种线程被称为锁的争抢。

There are four different kinds of locks:

以下有四种不同的锁

  • Fat locks: A fat lock is a lock with a history of contention (several threads trying to take the lock simultaneously), or a lock that has been waited on (for notification).
  • Fat locks: fat lock是指一个正在被争抢的锁,或者在被等待的锁
  • Thin locks: A thin lock is a lock that does not have any contention.
  • Thin locks: thin lock是指没有争抢现象的锁。
  • Recursive locks: A recursive lock is a lock that has been taken by a thread several times without having been released.
  • Recursive locks:recursive lock是指被同一线程多次获取且一直没有释放的锁
  • Lazy locks: A lazy lock is a lock that is not released when a critical section is exited. Once a lazy lock is acquired by a thread, other threads that try to acquire the lock have to ensure that the lock is, or can be, released. Lazy locks are used by default in Oracle JRockit JVM 27.6. In older releases, lazy locks are only used if you have started the JVM with the -XXlazyUnlocking option.

A thin lock can be inflated to a fat lock and a fat lock can be deflated to a thin lock. The JRockit JVM uses a complex set of heuristics to determine when to inflate a thin lock to a fat lock and when to deflate a fat lock to a thin lock.

thin lock和fat lock可以互相转化,JRockit JVM有一套复制的机制来决定两者的转化时机。

Spinning and Sleeping

自旋和休眠

Spinning occurs when a thread that wants a specific lock continuously checks that lock to see if it is still taken, instead of yielding CPU-time to another thread.

自旋发生在一个线程在争抢锁时,不停地检查一把锁是否被其他线程持有(而非退让cpu资源去等待)。

Alternatively, a thread that tries to take a lock that is already held waits for notification from the lock and goes into a sleeping state. The thread will then wait passively for the lock to be released.

相对的,休眠是指一个线程如果在争抢已被占用的锁时,执行wait进入休眠状态。此时,该线程被动的等待锁被释放时的notification。

Lock Chains

锁链

Several threads can be tied up in what is called lock chains. Although they appear somewhat complex, lock chains are fairly straightforward. They can be defined as follows:

若干个线程因为相互等待而关联起来时被称为锁链。所以这种情况下一般比较复杂,但是它们的链关系则是相对直观的,可以被这样描述:

  • Threads A and B form a lock chain if thread A holds a lock that thread B is trying to take. If A is not trying to take a lock, then the lock chain is “open.”
  • 如果线程A持有线程B尝试获得的锁,那么线程A和B组成锁链,并且此时链是开放的、有解的。
  • If A->B is a lock chain, and B->C is a lock chain, then A->B->C is a more complete lock chain.
  • 如果线程A和B构成锁链,且线程B和C构成锁链,那么A、B、C则是更完整的一个锁链
  • If there is no additional thread waiting for a lock held by C, then A->B->C is a complete and open lock chain.
  • 如果没有更多线程在等待、争抢C持有的任意锁,那么A、B、C则是一个完整的、开放有解的锁链

Lock Chain Types

锁链的类型

The JRockit JVM analyzes the threads and forms complete lock chains. There are three possible kinds of lock chains: Open, Deadlocked and Blocked lock chains.

有如下三种可能的锁链:开放有解的锁链、死锁无解的锁链、被阻塞的锁链

Open Chains

开放有解的锁链

Open lock chains represent a straight dependency, thread A is waiting for B which is waiting for C, and so on. If you have long open lock chains, your application might be wasting time waiting for locks. You may then want to reconsider how locks are used for synchronization in your application.

开放的锁链式中各个线程是线性依赖的,如A等待B持有的锁、B等待C持有的锁,以此类推。如果存在一个较长的开放锁链,那么这个应用可能会浪费很长时间在锁的等待上,此时需要重新考虑设计此应用的同步逻辑。

Deadlock Chains

死锁无解的锁链

A deadlocked, or circular, lock chain consists of a chain of threads, in which the first thread in the chain is waiting for the last thread in the chain. In the simplest case, thread A is waiting for thread B, while thread B is waiting for thread A. Note that a deadlocked chain has no head. In thread dumps, the Oracle JRockit JVM selects an arbitrary thread to display as the first thread in the chain.

死锁由线程之间的环形依赖关系形成。在这个环形关系中,第一个线程等待着最后一个线程所持有的锁而组成闭环的形式。举一个简单的例子,线程A等待线程B中的某个锁,而同时线程B又等待线程A中的某个锁。要注意一点是,此时并没有明确的第一个线程和最后一个线程的事实,它们只是在JVM展示时,由虚拟机选取任命的表现而已。

Deadlocks can never be resolved, and the application will be stuck waiting indefinitely.

死锁无法被处理,而此时应用也会因为死锁而永远的卡住。

Blocked Chains

被阻塞的锁链

A blocked lock chain is made up of a lock chain whose head thread is also part of another lock chain, which can be either open or deadlocked. For example, if thread A is waiting for thread B, thread B is waiting for thread A, and thread C is waiting for thread A, then thread A and B form a deadlocked lock chain, while thread C and thread A form a blocked lock chain.

被阻塞的锁链是由部分开发、部分死锁的复杂依赖的线程构成。例如,A和B构成了死锁,而C同时在等待A中持有的某个锁,那么此时C便被阻塞了。

<think>嗯,用户想了解Python中的RLock,特别是关于加和解必须由同一个线程完成的原理和机制。首先,我需要回忆一下RLock的基本概念。RLock是可重入,允许同一线程多次获取而不会死。这和普通的Lock不同,因为普通Lock如果同一线程多次acquire,就会阻塞。 接下来,用户的问题集中在为什么RLock必须由同一个线程来加和解。我需要从RLock的内部机制来解释。根据引用[2],RLock内部维护了一个Lock和一个计数器counter,还有一个owner记录当前持有线程。当线程第一次acquire时,counter加1,并记录owner。之后同一线程再次acquire时,counter继续增加,而不会阻塞。每次release时,counter减1,直到为0时才真正释放内部的Lock,其他线程才能获取。 关键点在于,只有获取线程才能释放它。因为每次release的时候,RLock会检查当前线程是否是owner,如果不是,就会抛出异常。这样可以确保的正确性,防止其他线程错误释放导致状态混乱。 可能用户还关心为什么这样设计。可重入的主要应用场景是递归调用或者多个需要同一的函数调用。比如,在递归函数中,同一线程可能需要多次获取,这时候用RLock就能避免死。而普通Lock在这种情况下会导致线程自己阻塞自己。 另外,引用[4]提到全局的设计避免了死,但这里讨论的是RLock,虽然和GIL不同,但可能用户会有混淆。需要区分清楚,RLock是用户级别的,而GIL是解释器级别的全局。两者解决的问题不同。 总结回答的结构应该是:先说明RLock的基本原理,然后详细解释内部机制(计数器、owner检查),接着对比普通Lock,说明为什么必须同一线程,最后应用场景和注意事项。需要确保引用正确的引用来源,比如引用[2]和[3]中的内容。</think>### Python RLock线程特性原理 #### 1. RLock 的核心机制 Python 的 $RLock$(可重入)要求**加和解必须在同一线程内完成**,其核心通过三个组件实现: - **底层**:$threading.Lock$ 控制实际资源访问 - **计数器**:记录当前线程的重入次数 - **所有者标识**:存储当前持有线程ID 数学表达式描述状态: $$ \text{状态} = \begin{cases} \text{已定} & \text{if 计数器 > 0} \\ \text{未定} & \text{otherwise} \end{cases} $$ #### 2. 加过程分析 ```python def acquire(): if current_thread == owner: counter += 1 else: base_lock.acquire() owner = current_thread counter = 1 ``` 此过程保证: 1. 首次获取时建立所有权 2. 重入时仅增加计数器 3. 不同线程请求时阻塞[^2] #### 3. 解过程验证 ```python def release(): if current_thread != owner: raise RuntimeError("非所有者线程无法释放") counter -= 1 if counter == 0: owner = None base_lock.release() ``` 通过严格的线程验证机制确保: -线程必须线程一致 - 完全释放需要计数器归零 - 防止非法释放导致数据竞争
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值