[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命令来修改栈的大小,举例如下:
The default stack sizes differ depending upon whether you are using IA32 and X64, as shown in Table 1:
栈的默认大小取决于具体使用的是IA32或者X64系统,细节在表1(Table 1)中。
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则是一个完整的、开放有解的锁链
锁链的类型
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 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持有的锁,以此类推。如果存在一个较长的开放锁链,那么这个应用可能会浪费很长时间在锁的等待上,此时需要重新考虑设计此应用的同步逻辑。
死锁无解的锁链
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.
死锁无法被处理,而此时应用也会因为死锁而永远的卡住。
被阻塞的锁链
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便被阻塞了。