preface

In Java 5.0, the java.util.Concurrent (JUC) package adds utility classes commonly used in concurrent programming to define custom thread-like subsystems, including thread pools, asynchronous IO, and lightweight task frameworks. Provides adjustable, flexible thread pools. Collection implementations designed for use in multithreaded contexts are also provided

Volatile keyword – Memory visibility

The JVM provides a separate cache for each thread for efficiency

Memory Visibility means that when one thread is using the state of an object and another thread is modifying the state at the same time, you need to make sure that when one thread changes the state of the object, other threads can see the state change

Visibility errors occur when read and write operations are performed in different threads, and there is no guarantee that the thread performing the read operation can see the value written by the other thread in real time, sometimes even impossible

We can use synchronization to ensure that objects are safely published. Alternatively, we can use a more lightweight volatile variable

Java provides a weaker synchronization mechanism, known as volatile variables, to ensure that changes to variables are notified to other threads. Volatile can be thought of as a lightweight lock, but not as a lock:

  • For multithreading, it is not mutually exclusive
  • “Atomic operation” that cannot guarantee variable state
public class VolatileTest { public static void main(String[] args) { ThreadDemo t = new ThreadDemo(); new Thread(t).start(); while (true) { if(t.isFlag()) { System.out.println("------"); break; } } } } class ThreadDemo implements Runnable { private boolean flag = false; @Override public void run() { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } flag = true; System.out.println("flag = " + isFlag()); } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; }}Copy the code

The mian thread reads false flags all the time, so flag = true is printed, and the program does not end

Solution:

Volatile, which ensures that data in memory is visible when multiple threads are operating to share data

public class VolatileTest { public static void main(String[] args) { ThreadDemo t = new ThreadDemo(); new Thread(t).start(); while (true) { if(t.isFlag()) { System.out.println("------"); break; } } } } class ThreadDemo implements Runnable { private volatile boolean flag = false; @Override public void run() { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } flag = true; System.out.println("flag = " + isFlag()); } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; }}Copy the code

Atomic variable-CAS algorithm

Class a small toolkit that supports thread-safe programming to unlock a single variable. In fact, classes in this package extend the concept of volatile values, fields, and array elements to classes that also provide atomic conditional update operations.

Instances of the classes AtomicBoolean, AtomicInteger, AtomicLong, and AtomicReference each provide access to and updates to individual variables of the corresponding type. Each class also provides the appropriate utility methods for that type.

The AtomicIntegerArray, AtomicLongArray, and AtomicReferenceArray classes further extend atomic operations to provide support for arrays of these types. These classes are also notable in providing volatile access semantics for their array elements, which is not supported for normal arrays.

Boolean compareAndSet(expectedValue, updateValue)

Under the Java. Util. Concurrent. Atomic package provides some atomic operations of the common categories: AtomicBoolean, AtomicInteger, AtomicLong, AtomicReference AtomicIntegerArray, AtomicLongArray,AtomicMarkableReference ,AtomicReferenceArray,AtomicStampedReference For specific methods, you can see the API documentation

I ++ atomicity problem, read I first and then ++, operation split, synchronization security problem

public class AtomicTest { public static void main(String[] args) { Atomic a = new Atomic(); for (int i = 0; i < 10; i++) { new Thread(a).start(); } } } class Atomic implements Runnable { private volatile int serialNumber = 0; @Override public void run() { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":" + getSerialNumber()); } public int getSerialNumber() { return serialNumber++; }}Copy the code

After the atomic variable jdk1.5 Java. Util. Concurrent. Atomic package provides a common atomic variables

  • 1. Volatile ensures memory visibility

  • 2.CAS algorithm ensures atomicity of data

    CAS algorithm is hardware support for concurrent operations sharing data

    Contains three operands:

    Memory value V

    Forecasts A

    Update the value B

    V = B if and only if V == A, otherwise, nothing is done

Simulation of the CAS

public class CompareAndSwapTest { public static void main(String[] args) { CompareAndSwap cas = new CompareAndSwap(); for (int i = 0; i < 10; i++) { new Thread(new Runnable() { @Override public void run() { int expectedValue = cas.get(); boolean b = cas.compareAndSet(expectedValue, (int)(Math.random() * 101)); System.out.println(b); } }).start(); } } } class CompareAndSwap { private int value; Public synchronized int get(){return value; } public synchronized int compareAndSwap(int expectedValue, int newValue) {int oldValue = value; if(oldValue == expectedValue) { this.value = newValue; } return oldValue; } public synchronized Boolean compareAndSet(int expectedValue, int newValue) { return expectedValue == compareAndSwap(expectedValue, newValue); }}Copy the code

Solve i++ atomicity problem

public class AtomicTest { public static void main(String[] args) { Atomic a = new Atomic(); for (int i = 0; i < 10; i++) { new Thread(a).start(); } } } class Atomic implements Runnable { private AtomicInteger serialNumber = new AtomicInteger(); @Override public void run() { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(getSerialNumber()); } public int getSerialNumber() { return serialNumber.getAndIncrement(); }}Copy the code

ConcurrentHashMap Lock segmentation mechanism

Hashtable is very inefficient compound operations can be thread unsafe and only one thread can operate at a time

Compound operations include iteration (retrieving elements repeatedly until the last element in the container), navigation (finding the next element in a certain order), and conditional operations (” Add if it doesn’t exist “, “delete if it does exist”)

Java 5.0 provides a variety of concurrent container classes in the java.util.Concurrent package to improve the performance of synchronous containers

The ConcurrentHashMap synchronization container class is a thread-safe hash table added to Java 5. Operation on multiple threads, between HashMap and Hashtable. Internal “lock fragmentation” mechanism is used to replace the exclusive lock of Hashtable. To improve performance

This package also provides an implementation of Collection designed for use in multithreaded contexts: ConcurrentHashMap, ConcurrentSkipListMap, ConcurrentSkipListSet, CopyOnWriteArrayList and CopyOnWriteArraySet. ConcurrentHashMap is generally superior to synchronous HashMap and ConcurrentSkipListMap is generally superior to synchronous TreeMap when many threads are expected to access a given collection. CopyOnWriteArrayList is superior to a synchronized ArrayList when the expected readings and traversals are much greater than the number of updates to the list

ConcurrentLevel of ConcurrentHashMap is 16

Each segment is an independent lock, and when multiple threads concurrently access, operations on different segments can be done in parallel

CopyOnWriteArrayList example

public class CopyOnWriteArrayListTest { public static void main(String[] args) { HelloThread ht = new HelloThread(); for (int i = 0; i < 2; i++) { new Thread(ht).start(); }}} /** * CopyOnWriteArrayList write and copy. */ class HelloThread implements Runnable {// Private static List<String> List = private static List<String> List = Collections.synchronizedList(new ArrayList<String>()); private static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>(); static { list.add("AA"); list.add("BB"); list.add("CC"); } @Override public void run() { Iterator<String> it = list.iterator(); while(it.hasNext()){ System.out.println(it.next()); list.add("AA"); }}}Copy the code

CountDownLatch atresia

Java 5.0 provides a variety of concurrent container classes in the java.util.Concurrent package to improve the performance of synchronous containers.

CountDownLatch is a synchronization helper class that allows one or more threads to wait until they complete a set of operations that are being performed in other threads.

Latching can delay the progress of a thread until it reaches the terminating state. Latching can be used to ensure that some activities do not continue until other activities have completed:

Ensure that a computation does not proceed until all the resources it needs have been initialized; Ensure that a service is started after all other services on which it depends have been started; Wait until all participants of an operation are ready to proceed

Locking: When an operation is completed, the current operation cannot continue until all operations on other threads have completed

CountDownLatchTest {public static void main(String[] args) {// 5 specifies the number of other threads CountDownLatch(5); LatchDemo ld = new LatchDemo(latch); long start = System.currentTimeMillis(); for (int i = 0; i < 5; i++) { new Thread(ld).start(); } try {// Wait until the latch is 0 to latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } long end = System.currentTimeMillis(); System.out.println(" "+ (end-start)); } } class LatchDemo implements Runnable { private CountDownLatch latch; public LatchDemo(CountDownLatch latch) { this.latch = latch; } @Override public void run() { synchronized(this) { try { for (int i = 0; i < 100; i++) { if (i % 2 == 0) { System.out.println(i); } } } finally { latch.countDown(); }}}}Copy the code

Condition controls thread communication

The Condition interface describes the Condition variables that may be associated with the lock. These variables are similar in usage to implicit monitors accessed using Object.wait, but provide more power. In particular, a single Lock can be associated with multiple Condition objects. To avoid compatibility issues, the Condition method is named differently from the corresponding Object version

In Condition, the corresponding methods to wait, notify, and notifyAll are await, signal, and signalAll, respectively

The Condition instance is essentially bound to a lock. To get a Condition instance for a particular Lock instance, use its newCondition() method

Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); // The three methods are used the same way as wait, notify, and notifyAllCopy the code

Threads alternate in order

Write A program, open 3 threads, the ID of these three threads are respectively A, B, C, each thread will print their ID on the screen 10 times, to find the output results must be displayed in order. Such as: ABCABCABC… In turn, the recursive

public class ABCAlternateTest { public static void main(String[] args) { AlternateDemo ad = new AlternateDemo(); new Thread(new Runnable() { @Override public void run(){ for (int i = 1; i <= 10; i++) { ad.loopA(i); } } },"A").start(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { ad.loopB(i); } } },"B").start(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { ad.loopC(i); } } },"C").start(); }} class AlternateDemo {// Record the ID of the currently executing thread private int number = 1; private Lock lock = new ReentrantLock(); private Condition condition1 = lock.newCondition(); private Condition condition2 = lock.newCondition(); private Condition condition3 = lock.newCondition(); public void loopA(int totalLoop) { try { lock.lock(); if(number ! = 1) { try { condition1.await(); } catch (InterruptedException e) { e.printStackTrace(); }} // Print for (int I = 1; i <= 1; i++) { System.out.print(Thread.currentThread().getName()); } number = 2; condition2.signal(); } finally { lock.unlock(); } } public void loopB(int totalLoop) { try { lock.lock(); if(number ! = 2) { try { condition2.await(); } catch (InterruptedException e) { e.printStackTrace(); }} // Print for (int I = 1; i <= 1; i++) { System.out.print(Thread.currentThread().getName()); } number = 3; condition3.signal(); } finally { lock.unlock(); } } public void loopC(int totalLoop) { try { lock.lock(); if(number ! = 3) { try { condition3.await(); } catch (InterruptedException e) { e.printStackTrace(); }} // Print for (int I = 1; i <= 1; i++) { System.out.print(Thread.currentThread().getName()); } number = 1; condition1.signal(); System.out.print(" "); } finally { lock.unlock(); }}}Copy the code

ReadWriteLock read-write lock

ReadWriteLock is an interface

ReadWriteLock maintains a pair of related locks, one for read-only operations and one for write operations. As long as there is no writer, the read lock can be held by multiple reader threads simultaneously. The write lock is exclusive

A ReadWriteLock read normally does not change the shared resource, but a write must be exclusive to obtain the lock. Data structures for which read operations dominate. ReadWriteLock provides higher concurrency than exclusive locks. In the case of read-only data structures, the immutability contained in them can be completely removed from consideration for locking operations

  • Writing/reading is mutually exclusive
  • Read without mutexes
public class ReadWriteLockTest { public static void main(String[] args) { ReadWriteLockDemo rw = new ReadWriteLockDemo(); new Thread(new Runnable() { @Override public void run() { rw.set((int)(Math.random() * 101)); } },"write").start(); for (int i = 0; i < 100; i++) { new Thread(new Runnable() { @Override public void run() { rw.get(); } }).start(); } } } class ReadWriteLockDemo { private int number = 0; private ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); Public void get() {try {readwritelock.readlock ().lock(); System.out.println(Thread.currentThread().getName() + ":" + number); } finally { readWriteLock.readLock().unlock(); }} public void set(int number) {try {readWritelock.writelock ().lock(); System.out.println(Thread.currentThread().getName()); this.number = number; } finally { readWriteLock.writeLock().unlock(); }}}Copy the code

Eight thread lock

There is no race condition between statically synchronized methods and non-statically synchronized methods. Non-statically synchronized methods check whether their locks are the same number. If they are the same number, one gets the lock and the other waits. Otherwise, the other one doesn’t have to wait

* Thread 8 lock key: 1. Non-static method lock this, static method lock corresponding Class instance * 2. Only one thread can hold the lock at any one time, no matter how many methods * 1. Two synchronized methods, two threads, print one two * 2. Add thread.sleep () to print one two * 3 for getOne. Add common method getThree, print three one two * 4. Comment getThree, number2.getTwo, print two one * 5. Change getOne to static synchronization method to number.gettwo and print two one * 6. GetOne for static synchronization, getTwo for synchronization, number2.getTwo for synchronization, print two one * 8. Two statically synchronized methods, two number objects, Print one two */ public class Thread8MonitorTest {public static void main(String[] args) {Number Number = new Number(); // 4 // Number number2 = new Number(); // 7 Number number2 = new Number(); new Thread(new Runnable() { @Override public void run() { number.getOne(); } }).start(); New Thread(new Runnable() {@override public void run() {// 1,2,3 // number.gettwo (); // 4 // number2.getTwo(); // 5 // number.getTwo(); // 7 number2.getTwo(); } }).start(); // 3 /*new Thread(new Runnable() { @Override public void run() { number.getThree(); } }).start(); */ } } class Number{ public static synchronized void getOne() { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("one"); } public static synchronized void getTwo() { System.out.println("two"); } public void getThree() { System.out.println("three"); }}Copy the code

The thread pool

A fourth way to get threads: a thread pool, an ExecutorService that executes each submitted task using one of several possible pool threads, usually configured using the Execorate Factory method.

Thread pools solve two different problems: they typically provide increased performance when performing a large number of asynchronous tasks because of the reduced overhead per task invocation, and they also provide a way to bind and manage resources, including the threads used when executing the task set. Each ThreadPoolExecutor also maintains some basic statistics, such as the number of tasks completed.

For ease of use across a large number of contexts, this class provides many tunable parameters and extension hooks. But it is strongly recommended that programmers use the more convenient Executors factory method:

  • Executors. NewCachedThreadPool () (unbounded thread pool, can automatically threads recycling)
  • Executors. NewFixedThreadPool (int) (fixed size thread pool)
  • Executors. NewSingleThreadExecutor () (single background threads) they are the most usage scenarios predefined Settings.

Create several threads in advance and put them into the thread pool. It can avoid frequent creation and destruction and realize reuse. It’s like a public communication tool in life.

Benefits: 1. Improved response time (reduced time to create new threads)

2. Reduce resource consumption (reuse threads in the thread pool without creating them every time)

CorePoolSize: the size of the core pool, /maximumPoolSize: the maximum number of threads, keepAliveTime: the maximum length of time a thread can hold a task before terminating the pool: A thread queue is provided to hold all threads in the waiting state, avoiding the extra overhead created on destruction and improving the speed of response

Java. Util. Concurrent. Executor: responsible for the use of threads and scheduling the root of the interface – son ExecutorService interface: The main interface of the thread pool, inheriting the Executor – ThreadPoolExecutor thread pool implementation class – ScheduledExecutorService subinterface: Responsible for thread scheduling, inherit to the ExecutorService – ScheduledThreadPoolExecutor ThreadPoolExecutor ScheduledExecutorService

public class ThreadPoolTest { public static void main(String[] args) { // 1. Create a thread pool 5 thread ExecutorService pool = Executors. NewFixedThreadPool (5); ThreadPoolDemo tpd = new ThreadPoolDemo(); For (int I = 0; i < 5; i++) { pool.submit(tpd); // shutdownNow() closes immediately, regardless of whether the task has been completed or not; */ List<Future<Integer>> list = new ArrayList<>(); for (int i = 0; i < 5; Future<Integer> Future = pool.submit(new Callable<Integer>() {@override public Integer call() throws Exception { int sum = 0; for (int j = 0; j <= 100; j++) { sum += j; } return sum; }}); list.add(future); } pool.shutdown(); for(Future<Integer> future : list) { try { System.out.println(future.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } } class ThreadPoolDemo implements Runnable{ private int i = 0; @Override public void run() { while(i <= 100){ System.out.println(Thread.currentThread().getName() + " : " + i++); }}}Copy the code

Thread scheduling

ScheduledExecutorService newScheduledThreadPool() creates a thread of fixed size that can delay or schedule the execution of tasks

public class ScheduledThreadPoolTest { public static void main(String[] args) { ScheduledExecutorService pool = Executors.newScheduledThreadPool(5); for (int i = 0; i < 5; i++) { Future<Integer> result = pool.schedule(new Callable<Integer>() { @Override public Integer call() throws Exception  { int num = new Random().nextInt(100); System.out.println(Thread.currentThread().getName() + ":" + num); return num; } }, 3, TimeUnit.SECONDS); // delay 3s execution try {system.out.println (result.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } pool.shutdown(); }}Copy the code

The use of the TimeUnit

ForkJoinPool branch/merge framework work theft

Fork/Join framework: it is to Fork a large task into several small tasks (when they are undeassembled) when necessary, and then Join together the results of each small task

It uses work-stealing: When a new task is being executed, it can break it into smaller tasks, add the smaller task to the thread queue, and steal one from the queue of a random thread and place it in its queue

The advantage of the fork/ Join framework over a generic thread pool implementation lies in the way it handles the tasks it contains. In a normal thread pool, a thread is in a wait state if the task it is executing cannot continue for some reason. In the fork/ Join framework implementation, if a subproblem fails to run because it is waiting for another subproblem to complete. The thread handling that subproblem will actively look for other subproblems that have not yet been run to execute. This approach reduces thread wait time and improves performance

The Stream API declaratively switches between parallel and sequential streams using parallel() and sequential()

public class ForkJoinPoolTest { public static void main(String[] args) { Instant start = Instant.now(); ForkJoinPool pool = new ForkJoinPool(); // RecursiveTask<V> extends ForkJoinTask<V> ForkJoinTask<Long> task = new ForkJoinTest(0L, 50000000000L); Long sum = pool.invoke(task); System.out.println(sum); Instant end = Instant.now(); System.out.println(Duration.between(start, end).toMillis()); } @Test public void test2() { // 18256 Instant start = Instant.now(); long sum = 0; for (long i = 0; i <= 50000000000L; i++) { sum += i; } Instant end = Instant.now(); System.out.println(Duration.between(start, end).toMillis()); } @test public void test3() {ForkJoin Instant start = Instant.now(); Long sum1 = longstream.rangeclosed (0, 50000000000L).parallel().reduce(0, long ::sum); LongBinaryOperator Instant End = Instant. Now (); LongBinaryOperator Instant End = Instant. System.out.println(Duration.between(start, end).toMillis()); }} Class ForkJoinTest extends RecursiveTask<Long> {Recursive RecursiveAction does not return a value private long start; private long end; public ForkJoinTest(long start, long end) { this.start = start; this.end = end; } private static final long THRESHOLD = 10000; @Override protected Long compute() { long length = end - start; if(length <= THRESHOLD){ long sum = 0; for (long i = start; i <= end; i++) { sum += i; } return sum; } else { long middle = (start + end) / 2; ForkJoinTest left = new ForkJoinTest(start, middle); left.fork(); ForkJoinTest right = new ForkJoinTest(middle + 1, end); right.fork(); return left.join() + right.join(); }}}Copy the code

The last

Thank you for reading here, after reading what do not understand, you can ask me in the comments section, if you think the article is helpful to you, remember to give me a thumbs up, every day we will share Java related technical articles or industry information, welcome to pay attention to and forward the article!