1. Risks of Threads:
1> Safety Hazards
We can add annotation: @NotThreadSafe, @ThreadSafe, @Immutable
2> Liveness Hazards
Deadlock, Starvation, Livelock
Bugs that cause liveness failures can be elusive because they depend on the relative timing of events in different threads. and therefore do not always manifest themselves in development or testing.
3> Performance Hazards
1> Context switches are more frequent in application with many threads.
2> When threads share data, they must use synchronization mechanisms.
2. Thread Intro:
1> Timer.
1) TimerTasks are executed in a thread managed by the Timer, not the application.
2) If a TimerTask access data that is also accessed by other application threads, then not only must the TimerTask do so in a thread-safe manner, but so must any other classes that access the data.
The easiest way to achieve this is to ensure that objects accessed by the TimerTask are themselves thread-safe, thus encapsulating the thread safety within the shared objects.
2> Servlets & JSPs & ServletFilters
1> One Servlet/JSP may be accessed by multiple threads at the same time.
2> Servlet often access state information shared with other servlets, such as application-scoped objects(those stored in ServletContext) or session-scoped objects(those stored in the per-client HttpSession).
When a servlet accesses objects shared across servlets or requests, it must coordinate access to these objects properly.
Servlets need to be thread-safe.
3> RMI
When the RMI code calls your remote object, in what thread does that call happen?
You don't know, but it definitely not in a thread you created.
How many threads does RMI create? Could the same remote method on the same remote object be called simultaneously in multiple RMI threads?
1> Properly coordinating access to state that may be shared with other objects.
2> Peoperly coordinating access to the state of the remote object itself(since the same object may be called in multiple threads simultaneously).
4> Swing components, such as JTable, are not thread-safe.
3. Thread Safety
Informally, an object's state is its data, stored in state variables such as instance or static fields.
An object's state may include fields from other, dependent object.
If multiple threads access the same mutable state variable without appropriate synchrnization, your program is broken. There are three ways to fix it:
1> Don's share the state variable across threads. -> Stateless objects are always thread-safe.
2> Make the state variable immutable.
3> Use synchronization whenever accessing the state variable.
It's far easier to design a class to be thread-safe than to retrofit it for thread safety later.
4. Atomicity
public class UnsafeCountingFactorizer extends HttpServlet { private long count = 0; public long getCount() { return count; } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { count++; } }
public class UnsafeCountingFactorizer extends HttpServlet { private AtomicLong count = new AtomicLong(0); public long getCount() { return count.get(); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { count.incrementAndGet(); } }
If the counter is initially 9, with some unlucky timing each thread could read the value, see that it is 9, add one to it, and each set the counter to 10.
As "long" type is not threadsafe itself, it means multiple thread can access to it at the same time. We can use AtomicLong instead to ensure threadsafe.
An increment got lost along the way, and the hit counter is now permanently off by one.
If the counter is being used to generate sequences or unique object ids, returning the same value from multiple invocations could cause serious data integrity problems.
The possibility of incorrect results in the presence of unlucky timing is so important in concurrent programming that it has a name: a race condition.
1> Race Condition: -> Check-Then-Act(Lazy init) & Read-Modify-Write(Increment)
Getting the right answer relies on lucky timing.
The common type of race condition is check-then-act, where a potentially stale observation is used to make a decision on what to do next.
Using a potentially stale observation to make a decision or perform a computation.
This type of race condition is called check-then-act; you observe something to be true(file X doesn't exist), and then take action based on that observation(create X); but in fact the observation could have become invalid between the time you observed it and the time you acted on it., causing a problem(unexpected exception, overwritten data, file corruption).
1> Lazy initialization & Race Condition
A common idiom that uses check-then-act is lazy initialization.
public class LazyInitRace { private ExpensiveObject expensiveObject = null; public ExpensiveObject getInstance() { if (null == expensiveObject) { expensiveObject = new ExpensiveObject(); } return expensiveObject; } private static class ExpensiveObject { } }
2> Data Race:
You risk a data race whenever a thread writes a variable that might next be read by another thread, or reads a variable that might have last been written by another thread if both threads do not use synchronization.
There must be a way to prevent other threads from using a variable while we are in the middle of modifying it, so we can ensure that other threads can observe or modify the state only before we start or after we finish, but not in the middle.
Back to the example of UnsafeCountingFactorizer, the state of the servlet it the state of the counter and the counter is thread-safe, our servlet is once again thread-safe.
5. Locking
Back to the example of UnsafeCountingFactorizer, what if we want to add more state to our servlet, can we just add more thread-safe state variables?
Imagine we want to cache the most recently computed result, just in case two connsecutive clients request factorization of the same number.
We need to remember two things: the last number factored, and its factors.
public class UnsafeCachingFactorizer extends HttpServlet { private final AtomicReference<BigInteger> lastNumber = new AtomicReference<BigInteger>(); private final AtomicReference<BigInteger[]> lastFactors = new AtomicReference<BigInteger[]>(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { BigInteger value = new BigInteger(req.getParameter("number")); if (value.equals(lastNumber.get())) { resp.addHeader("Factors", Arrays.asList(lastFactors.get()) .toString()); } else { BigInteger[] factors = calculateFactor(value); lastNumber.set(value); lastFactors.set(factors); resp.addHeader("Factors", Arrays.asList(factors).toString()); } } private BigInteger[] calculateFactor(BigInteger value) { try { Thread.sleep((long) (1000 * Math.random())); } catch (InterruptedException e) { e.printStackTrace(); } return new BigInteger[]{}; } }
Unfortunately, this approach does not work. Even though the atomic references are individually thread-safe, UnsafeCachingFactorizer has race conditions that could make it produce the wrong answer.
Locking is not just about mutual exclusion; it is also about memory visibility.
After we exit a synchronized block, we release the monitor, which has the effect of flushing the cache to main memory, so that writes made by this thread can be visible to other threads.
Before we can enter a synchronized block, we acquire the monitor, which has the effect of invalidating local processor cache so that variables will be reloaded from main memory.
public class NotVisibility { private static boolean ready; private static int number; private static class ReaderThread extends Thread { @Override public void run() { while (!ready) { Thread.yield(); } System.out.println(number); } } public static void main(String[] args) { new ReaderThread().start(); number = 42; ready = true; } }
This ReaderThread could loop forever because the value of ready might never become visible for reader thread.
Even more strangely, NoVisibility could print zero because the write to ready might be made visible to the reader thread before write to number, a phenomenon known as reordering.
Modern shared-memory multiple processor archectures, each processor has one or more levels of cache that are periodically reconciled with main memory.
The visibility of writes to shared data can be problematic.
To ensure that, all threads sees the most up-to-date value of shared variables, the reading and writing threads must synchronize on a common lock.
1) Volatile Variables:
When a field is declared volatile, the compiler and runtime are put on notice that this variable is shared and that operations on it should not be reordered with other memory operations.
Volatile variables are not cached in registers or in caches where they are hidden from other processors, so read a volatile variable always returns the most recent write by any thread.
We can think of volatile var as a lighter synchronized mechanism.
"Use volatile variables only when they simply implementing and verifying your synchronization policy; avoid using volatile variables when verifying correctness would require subtle reasoning about visibility. Good uses of volatile variables include ensuring the visibility of their own state, that of the object they refer to, or indicating that an important lifycycle event(such as initialization or shutdown) has occurred"
2) Publication and Escape
1> Publications: Make it available to code outside its current scope
2> Escape: An object is published when it should not have been.
1) Publishing states in this way is problematic because any caller can modify its contents. The states array has escaped its intended scope, because what we supposed to be private field has been effectively made public. Once an object escapes, you have to assume that another class or thread may, maliciously or carelessly, misuse it.
public class UnsafeState { private String[] states = new String[] { "AL", "AK" }; public String[] getStates() { return states; } }
2) When ThisEscape publishes EventListener, it implicitly publishes the enclosing ThisEscape instance as well, because inner class instances contain a hidden reference to the enclosing instance.
public class ThisEscape { public ThisEscape(EventSource source) { source.registerListener(new EventListener(){ public void onEvent(Event e){ doSomething(e); } }); } }
Use Factory method to prevent the "this" reference from Escaping During Construction
public class SafeListener { private final EventListener listener; private SafeListener() { listener = new EventListener() { public void onEvent(Event e) { doSomething(e); } }; } public static SafeListener newInstance(EventSource source) { SafeListener safe = new SafeListener(); source.registerListener(safe.listener); return safe; } }
3) Safe Construction Practices
The "this" reference shouldn't escape from the thread until after the constructor returns.
Don't allow the "this" reference to escape during construction.
A common mistake that can let the "this" reference escape during construction is to start a thread from a constructor. When an object creates a thread from its constructor, it almost always shares its "this" reference with the new thread, either explicitly(by passing it to the constructor) or implicitly(because the Thread or Runnable is an inner class of the owing object). The new thread might then be able to see the owning object before it is fully constructed. There is nothing wrong with creating a thread in a constructor, but it is best not to start the thread immediately. Instead, expose a start or initialize method that starts the owned thread.
public class ConstructorExpose { private int initialCapicity; private Thread daemonthread; public ConstructorExpose(int ainitialCapicity) throws InterruptedException { super(); this.daemonthread = new Thread(new Runnable() { @Override public void run() { System.out.println(initialCapicity); } }); daemonthread.start(); Thread.sleep(1000); this.initialCapicity = ainitialCapicity; } }
public class ConstructorExposeTest { @Test public void constructTest() throws InterruptedException { new ConstructorExpose(42); } } //0
Above is an unsafe publish/escape which we should care of.
public class ConstructorExpose2 { private int initialCapicity; private Thread daemonthread; public ConstructorExpose2(int ainitialCapicity) throws InterruptedException { super(); this.daemonthread = new Thread(new Runnable() { @Override public void run() { System.out.println(initialCapicity); } }); this.initialCapicity = ainitialCapicity; } public void initialize() { daemonthread.start(); } }
public class ConstructorExpose2Test { @Test public void constructTest() throws InterruptedException { new ConstructorExpose2(42).initialize(); } }
4) Thread Confinement
If data is only accessed from a single thread, no synchronization is needed. This technique, thread confinement, is one of the simplest ways to achieve thread safe.
When an object is confined to a thread, such usage is automatically thread-safe even if the confined object itself is not.
1) Swing uses thread confinement extensively. The Swing visual components and data model objects are not thread-safe; instead, safety is achieved by confining them to the Swing event dispatch thread. Many concurrency errors in Swing applications stem from improper use of these confined objects from another thread.
2) Pooled JDBC Connection objects. The JDBC specification doesn't require that Connection objects be thread-safe. Connection pool impelmentations provided by applications servers are thread-safe, connection pools are necessarily accessed from multiple threads, so a non-thread-safe implementation would not make sense.
It has no means of confining an object to a thread. Thread confinement is an element of your program's design that must be enforced by its implementation.
We can use ThreadLoacal provided by JDK - But even with these, it is still the programmer's responsibility to ensure that thread-confined objects do not escape from their intended thread.
5) ThreadLocal
6) Immutability: An immutable object is one whose state cannot be changed after construction.
Immutable objects are inherently thread-safe; their invariants are estabished by the constructor. and if their state cannot be changed, their invariants always hold.
An object is immutable if:
1) Its state cannot be modified after construction 2) All its fields are final 3) It is properly constructed(The this reference doesn't escape during construction)
There is a difference between an object being immutable and the reference to it being immutable.
Program state in immutable objects can still be updated by "replacing" immutable objects with a new instance holding new state.
"Some developers fear that this approach will create performance problems, but these fears are usually unwarrented. Allocation is cheaper than you might think, and immutable objects offer additional performance advantages such as reduced locking or defensive copies and reduced impact on generational garbage colleciton".
Reference Links:
1) http://stackoverflow.com/questions/4818699/practical-uses-for-atomicinteger
2) https://www.securecoding.cert.org/confluence/display/java/Concurrency,+Visibility,+and+Memory
3) http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html
相关推荐
C++11引入了并发编程的支持,包括`std::future`、`std::promise`以及`std::async`等工具,而`concurrency::task`是微软的PPL(Parallel Patterns Library,并行模式库)的一部分,为C++提供了更高级别的异步编程模型...
Java Concurrency in Practice 英文无水印pdf pdf所有页面使用FoxitReader和PDF-XChangeViewer测试都可以打开 本资源转载自网络,如有侵权,请联系上传者或csdn删除 本资源转载自网络,如有侵权,请联系上传者...
本教程"JavaConcurrency:Java并发教程"旨在深入探讨Java平台上的并发机制和最佳实践。 Java并发的核心概念包括线程、同步、互斥、死锁以及线程安全。在Java中,线程是并发执行的基本单元,通过创建Thread对象或者...
<<java并行编程>>英文版chm格式,英文名称<Java Concurrency in Practice>,一直想买这本书,但总是缺货,找到了电子版,分享给大家。 Java Concurrency in Practice By Brian Goetz, Tim Peierls, Joshua Bloch,...
4. **并发工具类**:Java 5及更高版本引入了`java.util.concurrent`包,包含各种并发工具,如`ExecutorService`、`Future`、`Semaphore`、`CountDownLatch`和`CyclicBarrier`等。这些工具可以帮助开发者更有效地管理...
Using the concurrency building blocks in java.util.concurrent Performance optimization dos and don'ts Testing concurrent programs Advanced topics such as atomic variables, nonblocking algorithms, ...
《并发的艺术》(The Art of Concurrency: A Thread Monkey's Guide to Writing Parallel Applications)是一本面向程序员的专业书籍,旨在深入讲解并发编程的核心概念和技术。本书由Clay Breshears撰写,并于2009年...
java_concurrency_in_practice.pdf jcip-examples-src.jar jcip-annotations-src.jar 英文版是高清晰的,实战和实践都是同一帮人对英文版书的翻译,网传实战的翻译质量更好,实战是2012年出版的,应该是对前一版实践...
Java Concurrency in practice
《Java并发编程实践》(Java Concurrency in Practice)是一本深度探讨Java多线程和并发编程的经典著作。这本书由Brian Goetz、Tim Peierls、Joshua Bloch、David Holmes和Doug Lea合著,旨在帮助Java开发者理解和解决...
Java Concurrency in Practice JAVA并发编程实践中文版(全)第二部分
- **Executor框架**:Java 5引入的ExecutorService接口,管理线程池,有效控制并发程度和任务调度。 - **ThreadPoolExecutor**:线程池的核心实现,可以自定义线程池参数以优化性能。 5. **并发集合** - **线程...
java8 源码 并发操作合集 这是一个关于并发的系列。以实战为驱动,了解并发编程中的那些骚操作。文中的示例代码和部分解释来源于网络,你可以把这个系列当做一本工具书,想不起来的时候来看一看,顺便star一发也是...
Get an easy introduction to reactive streams in Java to handle concurrency, data streams, and the propagation of change in today's applications. This compact book includes in-depth introductions to ...
本项目"Java-Concurrency:Java并发学习演示"旨在提供一个深入理解Java并发机制的实践平台。以下是对相关知识点的详细说明: 1. **线程与进程**:在计算机系统中,进程是资源分配的基本单位,而线程是执行的基本单位...
java concurrency in practice 经典的多线程编程书籍,英文版
Java并发是Java编程中至关重要的一部分,它涉及到多线程、同步机制、线程池和并发集合等关键概念。Java并发包(java.util.concurrent)提供了一系列的类和接口,旨在简化多线程环境下的编程,提高应用程序的性能和可...
Java并发编程是Java平台的核心特性之一,它使得开发者可以创建能够充分利用多核处理器能力的高效应用程序。本课程将深入探讨Java中的并发概念,包括基础支持和高阶API,特别是`java.util.concurrent`包中的工具。 ...