"Java Core Technology" Reading Notes (4)-Concurrency

Basics of concurrent programming

Thread basic concepts

The difference between multithreading and multiprocessing: each process has its own set of variables, and threads share data.
Run multiple tasks in parallel

create

  1. new Thread(Runnable);
  2. The creation method of inheriting Thread is no longer recommended, and the tasks that run in parallel should be decoupled from the running mechanism;
  3. Thread pool: When there are many tasks, it is too expensive to create a separate thread for each task. (14.9)

Note: Callabe is similar to the Runnable interface, indicating an asynchronous task with a return value. The FutureTask class implements the Runnable and Future interfaces and can be used to convert between Callabe and Runnable and Futrue.

start up

thread.start()

Interrupt

There is no way to force thread termination. The stop and suspend methods have been deprecated. The interrupt method can be used to request thread termination.

In general, threads need to detect the interrupt status from time to time.

while (!Thread.currentThread().islnterrupted() && more work to do) {
	do more work
}

When the interrupt method is called on a blocked thread (called sleep or wait), the blocked call will be interrupted by Interrupted Exception. (There are also blocking I / O calls that will not be interrupted, and you should consider choosing interruptible calls).

The interrupted thread can decide how to respond to the interrupt on its own. Generally, the thread is terminated:

Runnable r = () -> {
	try
	{
		. . . 
		while (!Thread.currentThread().isInterrupted() && more work to do) {
			do more work
		} 
	}
	catch(InterruptedException e) { 
		// thread was interrupted during sleep or wait
	}
	finally
	{
		cleanup,if required
	}
	// exiting the run method terminates the thread
};

Note: If the sleep method is called in the loop body, when the interrupt status is set, it will not sleep, instead, it will clear this status and throw an exception. Therefore, it is not necessary to perform isInterrupted detection at the while loop at this time, and the code of the detection status can be deleted.

Thread.interrupted() 和 thread.isInterrupted 比较:静态方法会清除中断状态。

Do not ignore the interrupt exception, a better way to deal with:

  1. The sleep method will clear the state when it is interrupted, so you can reset the state in exception handling for the caller to detect

    void mySubTask()
    {
        ……
        try { sleep(delay); }
        catch (InterruptedException e) { Thread.currentThread().interrupt(); }
        ……
    }
    
  2. throws InterruptedException marking method
    void mySubTask() throws InterruptedException

State getState ()

  1. New
    has not started running
  2. Runnable
    may not be running, depending on the operating system scheduling mechanism (preemptive-time slice, collaborative)
  3. Blocked、Waiting、Timed waiting
    • Attempt to acquire an internal object lock, which is held by other threads and blocked;
    • When a thread waits for another thread to notify the scheduler of a condition (wait, join, or wait for Lock or Condition), wait;
    • wait, join, tryLock, await, sleep When a method with timeout parameters is called, it enters the timing wait.
  4. Terminated
    • The run method exits normally
    • Uncaught abnormal accidental death
    • stop method (obsolete)

What is the difference between blocked state and wait state?
[External chain image transfer failed, the source site may have an anti-theft chain mechanism, it is recommended to save the image and upload it directly (img-gt0NmIDZ-1582987902984) (evernotecid: // 1520493E-927F-420A-8EE1-BA6F74088A9D / appyinxiangcom / 11767354 / ENResource / p3036)]

Attributes

  1. Priority is
    highly system dependent: 1 ~ 10 may be more or less when mapped to OS. In the Java virtual machine provided by Oracle for Linux, the priority of threads is ignored.

Do not build the program as a function of correctness depends on priority.
The scheduler prefers high-priority threads: if high-priority threads do not enter an inactive state, low-level threads can never execute
Thread.yield() 让出CPU

  1. Daemon thread setDaemon
    -provides services for other threads (example: timing thread), when only the daemon thread remains, the virtual machine exits.
    – Use: Do not access inherent resources, such as files, databases, it will be interrupted at any time.
    Call setDaemon (true) before the thread starts

  2. Thread.UncaughtExceptionHandler
    setUncaughtExceptionHandler
    If there is a parent thread group (ThreadGroup), call the parent thread group this method; otherwise, it is called if the Thread object has a default processor; otherwise, if Throwable is an instance of ThreadDeath (produced by the stop method), everything Don't do it; otherwise, output the stack trace to the standard error stream.

A thread group is a collection of threads that can be managed uniformly. By default, all threads created belong to the same thread group, but other groups may also be established. Now introduces better features for the operation of thread collection, so it is recommended not to use thread groups in your own programs.

Synchronize

How to control the interaction between threads

Locks and conditions

Race condition (multiple threads modify the same object to produce corrupted objects)
Cause: The execution of the method may be interrupted (non-atomic operation) (javap -c -v classname to view the class bytecode, which may be a java The statement generates multiple virtual machine instructions, the operation may be interrupted, and the object causing the corruption appears)

Solution: lock and condition (Lock / Condition or synchronized)

  1. Lock / ReentrantLock
    basic use structure:

    private Lock bankLock = new ReentrantLock();  //object field
    
    myLock.lock(); // a ReentrantLock object, a share object,second thread whill bolcked
    try
    {
        critical section
    }
    finally
    {
        myLock.unlock()// make sure the lock is unlocked even if an exception is thrown
    }
    

    Note: If you use a lock, you cannot use a try statement with resources. The try statement with resources hopes that the first statement declares a new variable, and lock, we want to use the variable shared by multiple threads;
    Note: pay attention to writing code in the critical area to avoid Because an exception is thrown and jumps out of the critical section, it causes the appearance of a corrupt object.

    The reentrant lock maintains a hold count and records the number of nested calls that call the lcok method. Code protected by a lock can call another method that uses the same lock protection.

    Fair lock (construction method with parameter fair)
    Fair lock believes that the longer the waiting time, the more the thread should be executed, but this will affect performance. Fair lock is not fair by default. Only when you determine your need to use fair locks, you can consider fair locks. Even if it is used, the thread scheduler may choose to ignore a thread that has been waiting for the lock for a long time.

  2. Conditional objects / conditional variables
    Why are conditional objects needed?
    The thread that acquires the lock and enters the critical section can continue execution after finding that a certain condition is met. It needs to wait for another thread to modify the state of the shared object, and then check the condition, and then execute again .
    Example: If a transfer operation acquires a lock, in the critical area, the balance is detected before the transfer. In this case, you need to wait for other threads to make the transfer first, and then continue the execution after the balance is sufficient. A lock object can have multiple related condition objects.

    Use:
    ① sufficientFunds = lock.newCondition (); // returns a condition object associated with the lock
    ② condition is not satisfied, call sufficientFunds.await when required blocked (), blocking execution threads, release the lock, hoping other threads;
    ③ Wait for other threads to call the signalAll method of the same condition.
    signalAll ()-removes the blocking state of all threads in this waiting condition, and does not immediately activate a waiting thread, so that these threads can access the object through competition after the current thread exits the synchronization method.
    Call timing: Called when the state of the object is conducive to waiting for the direction of the thread to change. For example, when the transfer is completed and the account balance changes.

The thread waiting to acquire the lock is essentially different from the thread calling the await method. Once a thread calls the await method, it enters the wait set for that condition. When the lock is available, the thread cannot immediately unblock. Instead, it is blocked until another thread calls the signalAll method on the same condition.

The thread that reacquires the lock needs to recheck whether the conditions are met. The general way to call await:

while(!(ok to proceed))
	condition.await();	// 将线程放到条件的等待集中

Another signal method is to randomly release the state of a thread in the waiting set, which is more effective, but there is a danger: the thread that may be blocked may still not run

At the cost of bookkeeping operations in the synchronization mechanism, the program may run slower. The correct use of conditional objects is challenging, and priority is given to using synchronizer-related structures before implementing your own conditional objects.

小结:Lock与Condition对象
	• 锁用来保护代码片段, 任何时刻只能有一个线程执行被保护的代码。 
	• 锁可以管理试图进入被保护代码段的线程。 
	• 锁可以拥有一个或多个相关的条件对象。 
	• 每个条件对象管理那些已经进入被保护的代码段但还不能运行的线程。

  1. Each object of synchronized has an internal lock, and the lock has an internal condition.
    wait, notifyAll, notify are equivalent to await, signalAll, signal

    You can also declare a static method, call the method to obtain the internal lock of the class object

    Advantages: The code is concise and not prone to errors.
    Limitations:
    • You cannot interrupt a thread that is trying to acquire a lock.
    • A timeout cannot be set when trying to obtain a lock.
    • Each lock has only a single condition, which may not be sufficient.

    Should I use Lock / Condition or synchronized?
    • It is best to use neither Lock / Condition nor synchronized keyword. In many cases you can use a mechanism in the java.util.concurrent package, which will handle all locking for you.
    • If the synchronized keyword is suitable for your program, please use it as much as possible, this can reduce the amount of code written and reduce the chance of errors.
    • Use Lock / Condition only when you need the unique features provided by the Lock / Condition structure.

    Synchronous blocking: synchronized (obj) {}
    uses an object's lock to implement an additional atomic operation, called client side locking, which relies on the fact that internal blocking is implemented, so this mechanism is fragile.

Secure access to shared domains

  1. Volatile
    problem: inconsistencies in multithreaded reading and writing of the same instance domain
  • Registers or local memory buffers store the values ​​in memory, and different processor threads take different values ​​in the same memory location.
  • Change instruction execution order to maximize throughput

volatile provides a lock-free mechanism for synchronous access to instance domains. If a domain is declared as volatile, then the compiler and the virtual machine know that the domain may be updated concurrently by another thread.
Volatile variables cannot guarantee atomicity, and cannot guarantee that reading, flipping, and writing will not be interrupted.

"If you write a value to a variable, and this variable may be read by another thread next, or read a value from a variable, and this variable may have been previously written by another thread, you must use synchronization "-Sync motto.

  1. final变量
    final Map<String, Double> accounts = new HashKap<>();

  2. Atomicity

  • Only assignment operation, you can use volatile
  • Set or increase or decrease value in atomic mode, you can use some classes under the automic package (machine-level instructions are provided)
  • To complete more complex updates, use compareAndSet (for example: tracking the maximum value observed by different threads)
public static AtonicLong largest = new AtomicLong()do {
    oldValue = largest.get();
    newValue = Math.max(oldValue , observed); 
} while (largest.compareAndSet(oldValue, newValue));

Note: The compareAndSet method will map to a processor operation, which is faster than using a lock.
Simplified writing after Java8:

largest. updateAndGet(x -> Math .max(x, observed)); 
1argest.accumulateAndCet(observed , Math::max);
(getAndUpdate 和 getAndAccumulate可以返回原值)

If there are a large number of threads to access the same atomic value, performance will be greatly reduced, because optimistic updates require too many retry (optimistic locking mechanism). Java SE 8 provides LongAdder and LongAccumulator classes.
LongAdder provides multiple addends (variables) corresponding to different threads, methods: increment, sum
LongAccumulator extends this idea to any accumulation operation. accumulate, get (need to meet the exchange law and combination law)
(similar classes: DoubleAdder, DoubleAccumulator)

Deadlock

It may happen that every thread has to wait for the condition object to be activated, which causes all threads to be blocked. Such a state is called a dead lock.
To observe the call stack of a deadlock thread through jconsole, the thread must be carefully designed to ensure that no deadlock will occur. (Signal may cause deadlock)

Thread local variable ThreadLocal

public static final ThreadLocal<SimpleDateFormat> dateFormat = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
dateFormat.get().format(new Date());

int random = ThreadLocalRandom.curren().nextlnt(upperBound):

Lock test and timeout

The lock method cannot be interrupted. If a thread is interrupted while waiting to acquire a lock, the interrupted thread is blocked until the lock is acquired. If a deadlock occurs, then the lock method cannot be terminated.
However, if you call tryLock with a timeout parameter, then if the thread is interrupted while waiting, an
InterruptedException will be thrown . This is a very useful feature because it allows programs to break deadlocks.

You can also call the locklnterruptibly method. It is equivalent to a tryLock method with a timeout set to unlimited.

tryLock (time), await (time) returns false, awaitUninterruptibly ()

ReentrantReadWriteLock

.readLock () // Get a read lock that can be shared by multiple read operations, but will exclude all write operations.
.writeLock () // Get a write lock, excluding all other read operations and write operations.

Stop, suspend, resume deprecation reasons

  1. The reason for stop deprecation: stopping the thread may cause the object to be in a damaged state. It is impossible to know when it is safe to call the stop method and when the object will be destroyed. ThreadDeath exception will release all lock objects
  2. Reason for suspend deprecation: easy to cause deadlock. If suspend suspends a thread holding a lock, and the thread calling the suspend method also wants to acquire the same lock, the program deadlocks.

Thread-safe collection

BlockingQueue

方法(按照队列满或者空时的响应方式)
	① 操作阻塞:put、take方法(作线程管理工具)
	② 抛异常:add、remove、element
	③ 错误提示:offer、poll、peek(多线程操作)
实现类:
	ArrayBlockingQueue(int capacity)
	LinkedBlockingQueue()    // 默认无限,容量可选
	DelayQueue()    // 延迟已经超过时间的元素可以从队列中移出
	PriorityBlockingQueue()    // 优先级队列
接口:
	BlockingQueue
	BiockingDeque
	TransferQueue

Efficient mapping, collection queue

  1. ConcurrentHashMap

    • Atomic update:
      execute the replace method cyclically or
      map.putlfAbsent(word, new LongAdder()).increment();or
      map.compute(word , (k, v) -> v = null ? 1: v + 1);or
      map.computelfAbsent(word , k -> new LongAdder()).increment();or
      map.merge(word, 1L, (existingVal, newVal) -> existingVal + newVal);
      Note: The functions in the compute and merge parameters return null, and the existing entry will be deleted from the map. And the function should not do too much work, it may block other updates to the map, and cannot update other parts of the map.

    • Batch operation: an approximate
      search, forEach, reduce (Keys, Values, KV, Entries) of the mapped state

      1. Parameterized threshold can be specified, the map contains more elements than the threshold, batch operations will be completed in parallel (1 ~ Long.MAX_VALUE)
      2. ForEach, reduce can specify a conversion function, the filter effect can be achieved when the result is null
      3. Primitive type specialization
    • Concurrent Set view
      Set words = ConcurrentHashMap.newKeySet (); The
      keySet method can generate an existing mapped key set that can be deleted without adding elements; overloading keySet (defaultVal) can add elements.

  2. Write array copy
    CopyOnWriteArrayList and CopyOnWriteArraySet

    • Use scenario: The number of iteration threads exceeds the number of modified threads
    • Consistency: Possible outdated consistency
  3. Synchronous wrapper

    • When iterating over the collection when another thread may be modified, you still need to use the "client" lock. If another thread modifies the collection during the iteration process, the iterator will fail and a ConcurrentModificationException will be thrown. Yes, concurrent modifications can be reliably detected;
    • It is best to use the collection defined in the java.util.concurrent package, not the synchronization wrapper. In particular, if they access different buckets, since ConcurrentHashMap has been carefully implemented, multiple threads can access it without blocking each other. An exception is the list of arrays that are frequently modified. In that case, synchronized ArrayList can outperform CopyOnWriteArrayList

    ConcurrentSkipListMap
    ConcurrentSkipListSet
    ConcurrentSkipListQueue

Executor

Thread Pool

Why use a thread pool?

  1. Building a new thread involves interacting with the operating system and is used to create a large number of threads with very short life cycles;
    (If there are many tasks, the cost to create a separate thread for each task is too high)
  2. Reduce the number of concurrent threads

Executors:
Create a thread pool and return an object of the ThreadPoolExecutor implementation class of the ExecutorService interface

Use process:
1. Call the static method newCachedThreadPool or newFixedThreadPool in the Executors class.
2. Call submit to submit the Runnable or Callable object.
3. If you want to cancel a task, or if you submit a Callable object, you must save the returned Future object.
4. When no more tasks are submitted, call shutdown.

Control task group

  1. shutdownNow
  2. invokeAny
  3. invokeAll
  4. ExecutorCompletionService saves the results in the order available

Fork-Join framework

  • Use one thread for each processing core to complete application of computationally intensive tasks
  • RecursiveTask
  • RecursiveAction
    compute、invokeAll、join、get

Note: The framework uses a "work stealing" method to balance the workload of available threads

Complete Future

Can be composed (composed), specify the order of execution

  1. Single future method:
  • thenApply
  • thenCompose
  • handle
  1. The method of combining multiple futures:
  • thenCombine
  • runAfterBoth
  • applyToEither
  • allOf
  • anyOf

Synchronizer

  1. Semaphore
    class: Semaphore (acquire, release)
    role: to allow a set of threads to wait until they were allowed to continue to operate until the
    scene: the total number of threads to restrict access to resources

  2. Countdown door bolt
    Class: CountDownLatch
    Function: Allow the thread set to wait until the counter is reduced to 0
    Scenario: When one or more threads wait until a specified number of events occur
    One-time, can not be reused, such as preliminary data preparation

  3. Barrier
    class: CyclicBarrier barrier = new CydicBarrier (nthreads); Each thread calls await when it reaches the barrier, and the barrier action is optional. (Phaser class is more flexible)
    Function: Allow the thread set to wait until a predetermined number of threads reach a common barrier (barrier,) Then you can choose to perform an action
    scenario that handles the barrier : when a large number of threads need to be before their results are available Upon completion

  4. Exchanger
    class: Exchanger
    role: allows two threads to exchange objects when the objects to be exchanged are ready.
    Scenario: When two threads work on two instances of the same data structure, one adds data to the instance and the other clears the data from the instance.

  5. Synchronous queue
    class: synchronousQueue (put method will block until another thread takes, and vice versa, the size is always 0)
    Role: Allow one thread to give objects to another thread
    Scenario: Without explicit synchronization When two threads are ready to pass an object from one thread to another

summary

This article organizes the basic knowledge of Java concurrency, from thread creation, startup, interruption, to interaction and coordination between threads, as well as synchronization tools, thread safety collections, thread pools and other content provided by the Java class library, for the development of concurrent programs Provide basic reserves.

Welcome to pay attention to my public account to learn more:
My public account

Published 159 original articles · praised 225 · 210,000 views

Guess you like

Origin blog.csdn.net/lyg673770712/article/details/104584270