`
onray
  • 浏览: 6288 次
  • 来自: ...
最近访客 更多访客>>
社区版块
存档分类
最新评论

Thread Synchronization

阅读更多
One of the strengths of the Java programming language is its support for multithreading at the language level. Much of this support centers on synchronization: coordinating activities and data access among multiple threads. The mechanism that Java uses to support synchronization is the monitor. This chapter describes monitors and shows how they are used by the Java virtual machine. It describes how one aspect of monitors, the locking and unlocking of data, is supported in the instruction set.

Monitors
Java's monitor supports two kinds of thread synchronization: mutual exclusion and cooperation. Mutual exclusion, which is supported in the Java virtual machine via object locks, enables multiple threads to independently work on shared data without interfering with each other. Cooperation, which is supported in the Java virtual machine via the wait and notify methods of class Object, enables threads to work together towards a common goal.

A monitor is like a building that contains one special room that can be occupied by only one thread at a time. The room usually contains some data. From the time a thread enters this room to the time it leaves, it has exclusive access to any data in the room. Entering the monitor building is called "entering the monitor." Entering the special room inside the building is called "acquiring the monitor." Occupying the room is called "owning the monitor," and leaving the room is called "releasing the monitor." Leaving the entire building is called "exiting the monitor."

In addition to being associated with a bit of data, a monitor is associated with one or more bits of code, which in this book will be called monitor regions. A monitor region is code that needs to be executed as one indivisible operation with respect to a particular monitor. In other words, one thread must be able to execute a monitor region from beginning to end without another thread concurrently executing a monitor region of the same monitor. A monitor enforces this one-thread-at-a-time execution of its monitor regions. The only way a thread can enter a monitor is by arriving at the beginning of one of the monitor regions associated with that monitor. The only way a thread can move forward and execute the monitor region is by acquiring the monitor.

When a thread arrives at the beginning of a monitor region, it is placed into an entry set for the associated monitor. The entry set is like the front hallway of the monitor building. If no other thread is waiting in the entry set and no other thread currently owns the monitor, the thread acquires the monitor and continues executing the monitor region. When the thread finishes executing the monitor region, it exits (and releases) the monitor.

If a thread arrives at the beginning of a monitor region that is protected by a monitor already owned by another thread, the newly arrived thread must wait in the entry set. When the current owner exits the monitor, the newly arrived thread must compete with any other threads also waiting in the entry set. Only one thread will win the competition and acquire the monitor.

The first kind of synchronization listed above, mutual exclusion, refers to the mutually exclusive execution of monitor regions by multiple threads. At any one time, only one thread can be executing a monitor region of a particular monitor. In general, mutual exclusion is important only when multiple threads are sharing data or some other resource. If two threads are not working with any common data or resource, they usually can't interfere with each other and needn't execute in a mutually exclusive way. On a Java virtual machine implementation that doesn't time slice, however, a higher priority thread that is never blocked will interfere with any lower priority threads, even if none of the threads share data. The higher priority thread will monopolize the CPU at the expense of the lower priority threads. Lower priority threads will never get any CPU time. In such a case, a monitor that protects no data may be used to orchestrate these threads to ensure all threads get some CPU time. Nevertheless, in most cases a monitor protects data that is accessed through the monitor region code. In cases where the data can be accessed only through the monitor regions, the monitor enforces mutually exclusive access to that data.

The other kind of synchronization listed above as supported by monitors is cooperation. Whereas mutual exclusion helps keep threads from interfering with one another while sharing data, cooperation helps threads to work together towards some common goal.

Cooperation is important when one thread needs some data to be in a particular state and another thread is responsible for getting the data into that state. For example, one thread, a "read thread," may be reading data from a buffer that another thread, a "write thread," is filling. The read thread needs the buffer to be in a "not empty" state before it can read any data out of the buffer. If the read thread discovers that the buffer is empty, it must wait. The write thread is responsible for filling the buffer with data. Once the write thread has done some more writing, the read thread can do some more reading.

The form of monitor used by the Java virtual machine is called a "Wait and Notify" monitor. (It is also sometimes called a "Signal and Continue" monitor.) In this kind of monitor, a thread that currently owns the monitor can suspend itself inside the monitor by executing a wait command. When a thread executes a wait, it releases the monitor and enters a wait set. The thread will stay suspended in the wait set until some time after another thread executes a notify command inside the monitor. When a thread executes a notify, it continues to own the monitor until it releases the monitor of its own accord, either by executing a wait or by completing the monitor region. After the notifying thread has released the monitor, the waiting thread will be resurrected and will reacquire the monitor.

The kind of monitor used in the Java virtual machine is sometimes called a Signal and Continue monitor because after a thread does a notify (the signal) it retains ownership of the monitor and continues executing the monitor region (the continue). At some later time, the notifying thread releases the monitor and a waiting thread is resurrected. Presumably, the waiting thread suspended itself because the data protected by the monitor wasn't in a state that would allow the thread to continue doing useful work. Also, the notifying thread presumably executed the notify command after it had placed the data protected by the monitor into the state desired by the waiting thread. But because the notifying thread continued, it may have altered the state after the notify such that the waiting thread still can't do useful work. Alternatively, a third thread may have acquired the monitor after the notifying thread released it but before the waiting thread acquired it, and the third thread may have changed the state of the protected data. As a result, a notify must often be considered by waiting threads merely as a hint that the desired state may exist. Each time a waiting thread is resurrected, it may need to check the state again to determine whether it can move forward and do useful work. If it finds the data still isn't in the desired state, the thread could execute another wait or give up and exit the monitor.

As an example, consider once again the scenario described above that involves a buffer, a read thread, and a write thread. Assume the buffer is protected by a monitor. When a read thread enters the monitor that protects the buffer, it checks to see if the buffer is empty. If the buffer is not empty, the read thread reads (and removes) some data from the buffer. Satisfied, it exits the monitor. On the other hand, if the buffer is empty, the read thread executes a wait command. As soon as it executes the wait, the read thread is suspended and placed into the monitor's wait set. In the process, the read thread releases the monitor, which becomes available to other threads. At some later time, the write thread enters the monitor, writes some data into the buffer, executes a notify, and exits the monitor. When the write thread executes the notify, the read thread is marked for eventual resurrection. After the write thread has exited the monitor, the read thread is resurrected as the owner of the monitor. If there is any chance that some other thread has come along and consumed the data left by the write thread, the read thread must explicitly check to make sure the buffer is not empty. If there is no chance that any other thread has consumed the data, then the read thread can just assume the data exists. The read thread reads some data from the buffer and exits the monitor.

A graphical depiction of the kind of monitor used by a Java virtual machine is shown in Figure 20-1. This figure shows the monitor as three rectangles. In the center, a large rectangle contains a single thread, the monitor's owner. On the left, a small rectangle contains the entry set. On the right, another small rectangle contains the wait set. Active threads are shown as dark gray circles. Suspended threads are shown as light gray circles.



Figure 20-1 also shows several numbered doors that threads must "pass through" to interact with the monitor. When a thread arrives at the start of a monitor region, it enters the monitor via the leftmost door, door number one, and finds itself in the rectangle that houses the entry set. If no thread currently owns the monitor and no other threads are waiting in the entry set, the thread passes immediately through the next door, door number two, and becomes the owner of the monitor. As the monitor owner, the thread continues executing the monitor region. If, on the other hand, there is another thread currently claiming ownership of the monitor, the newly arrived thread must wait in the entry set, possibly along with other threads already waiting there. The newly arrived thread is blocked and therefore doesn't execute any further into the monitor region.

Figure 20-1 shows three threads suspended in the entry set and four threads suspended in the wait set. These threads will remain where they are until the current owner of the monitor--the active thread--releases the monitor. The active thread can release the monitor in either of two ways: it can complete the monitor region it is executing or it can execute a wait command. If it completes the monitor region, it exits the monitor via the door at the bottom of the central rectangle, door number five. If it executes a wait command, it releases the monitor as it travels through door number three, the door to the wait set.

If the former owner did not execute a notify before it released the monitor (and none of the waiting threads were previously notified and were waiting to be resurrected), then only the threads in the entry set will compete to acquire the monitor. If the former owner did execute a notify, then the entry set threads will have to compete with one or more threads from the wait set. If a thread from the entry set wins the competition, it passes through door number two and becomes the new owner of the monitor. If a thread from the wait set wins the competition, it exits the wait set and reacquires the monitor as it passes through door number four. Note that doors three and four are the only ways a thread can enter or exit the wait set. A thread can only execute a wait command if it currently owns the monitor, and it can't leave the wait set without automatically becoming again the owner of the monitor.

In the Java virtual machine, threads can optionally specify a timeout when they execute a wait command. If a thread does specify a timeout, and no other thread executes a notify before the timeout expires, the waiting thread in effect receives an automatic notify from the virtual machine. After the timeout expires, the waiting thread will be resurrected even if no other thread has executed an explicit notify.

The Java virtual machine offers two kinds of notify commands: "notify" and "notify all." A notify command selects one thread arbitrarily from the wait set and marks it for eventual resurrection. A notify all command marks all threads currently in the wait set for eventual resurrection.

To a great extent, the manner in which a Java virtual machine implementation selects the next thread from the wait or entry sets is a decision of individual implementation designers. For example, implementation designers can decide how to select: o a thread from the wait set given a notify command o the order to resurrect threads from the wait set given a notify all command o the order to allow threads from the entry set to acquire the monitor o how to choose between threads suspended in the wait set versus the entry set after a notify command You might think it would make sense to implement entry set and wait sets as first-in-first-out (FIFO) queues, so that the thread that waits the longest will be the first chosen to acquire the monitor. Alternatively, it might make sense to have ten FIFO queues, one for each priority a thread can have inside the Java virtual machine. The virtual machine could then choose the thread that has been waiting the longest in the highest priority queue that contains any waiting threads. Implementations may take approaches such as these, but you can't depend on it. Implementations are free to implement the entry and wait sets as last-in-first-out (LIFO) queues, to select lower priority threads before higher priority threads, or to do anything else that may not seem to make sense. In short, implementations are free to select threads in an arbitrary manner that defies analysis and yields surprising orderings.

As a programmer, you must not rely on any particular selection algorithm or treatment of priorities, at least if you are trying to write a Java program that is platform independent. For example, because you don't know what order threads in the wait set will be chosen for resurrection by the notify command, you should use notify (as opposed to notify all) only when you are absolutely certain there will only be one thread suspended in the wait set. If there is a chance more than one thread will be suspended in the wait set at any one time, you should probably use notify all. Otherwise, on some Java virtual machine implementations a particular thread may be stuck in the wait set for a very long time. If a notify always selects the most recent arrival from the wait set and the wait set always contains multiple threads, some threads that have been waiting the longest may never be resurrected.

分享到:
评论

相关推荐

    ThreadSynchronization.zip

    本文将围绕“C#多线程”中的一个重要概念——线程同步,结合“ThreadSynchronization.zip”压缩包中的示例代码进行深入探讨。 线程同步是指在多线程环境中控制不同线程对共享资源的访问,以避免数据不一致或竞争...

    Thread Synchronization in User Mode

    "Thread Synchronization in User Mode" 主要关注用户模式下的线程同步问题,这里我们将详细讨论其中的几个主要概念和技术。 首先,线程同步的问题在于当多个线程并发执行时,它们可能会同时访问和修改同一块数据,...

    Thread synchronization in Linux and Windows systems

    在现代操作系统中,每个进程都拥有自己的地址空间和一个控制线程。然而,在实际应用中,我们经常遇到需要在单个进程内执行多个并发任务的情况,并且这些任务需要访问相同进程的组件,比如数据结构、打开的文件描述符...

    linux-itss:linux线程间同步(Inter-Thread Synchronization)方式汇总

    linux线程间同步方式(inter-thread-synchronization)汇总 包含的同步方式 互斥量 条件变量 读写锁 自旋锁 barrier(这个不知道怎么翻译) #扩展 我的另一个仓库有关于linux进程间通信的方式汇总,,供参考,欢迎讨论...

    深入java虚拟机(inside the java virtual machine)

    20 Thread Synchronization Monitors Object Locking Synchronization Support in the Instruction Set Synchronized Statements Synchronized Methods Coordination Support in Class Object On the CD-ROM ...

    Thread Synchronization Library for .Net-开源

    标题中的"Thread Synchronization Library for .Net-开源"指的是一个专为.NET平台设计的开源库,用于实现线程间的同步。这个库可能包含了各种同步原语和高级并发控制结构,如锁、信号量、事件、监视器等,帮助开发者...

    Java_programming_code_for_thread_synchronization_C_java programm

    Java编程实现线程同步经典代码Java programming code for thread synchronization Classic

    QT Thread Synchronization

    在Qt框架中,多线程同步是一个至关重要的概念,它涉及到如何在多个执行线程间安全地共享数据和资源,防止竞态条件和死锁的发生。本篇文章将深入讲解Qt4.6版本中的多线程同步机制,主要关注两种同步方法:等待条件...

    Java 7 Concurrency Cookbook

    Chapter 3, Thread Synchronization Utilities will teach the readers to use the high-level utilities of Java to manage the synchronization between the threads in Java. It includes an explanation of how ...

    Windows系统编程第四版英文版PDF+配套实例程序.rar

    CHAPTER 8 Thread Synchronization 259 CHAPTER 9 Locking, Performance, and NT6 Enhancements 301 CHAPTER 10 Advanced Thread Synchronization 335 CHAPTER 11 Interprocess Communication 379 CHAPTER 12 ...

    Java 9 Concurrency Cookbook.2e

    * Thread synchronization mechanisms * Thread creation and management delegation with executors * Fork/Join framework to enhance the performance of your application * Parallel streams to process big ...

    Java 9 Concurrency Cookbook - Second Edition

    Further, you'll discover a whole range of recipes for almost everything, such as thread management, synchronization, executors, parallel and reactive streams, and many more. At the end of the book, ...

    三个C#线程的例子三个C#线程的例子三个C#线程的例子

    ### 例子2:线程同步(Thread Synchronization) 线程同步用于控制不同线程对共享资源的访问,以避免数据竞争和死锁。`Mutex`、`Semaphore`、`Monitor`和`lock`关键字是常用的同步机制。这里以`lock`关键字为例: ...

    SimuTcpClient模拟tcp协议的三次握手 mfc 使用winpcap开发; 在多线程同步方面有些小问题

    -Simulated three-way handshake protocol tcp mfc using winpcap development in a multi-thread synchronization aspects of some small problems windows xp sp2 and above the original system can not send tcp...

    Delphi Cookbook Third Edition

    synchronization and the mechanisms used to obtain this synchronization, such as TMonitor, thread-safe queues, and TEvent. It is also one of the most complex chapters. By the end of this chapter, the ...

Global site tag (gtag.js) - Google Analytics