This is the fifth day of my participation in the August Challenge. For details, see:August is more challenging

Three ways to create a thread

  1. Inheriting the Thread

    Class Demo2 extends Thread {@override public synchronized void start() {system.out.println (" extends Thread "); }}Copy the code
  2. Implement the Runable interface

    Class Demo1 implements Runnable {@override public void run() {system.out.println (" Implements a Thread "); }Copy the code
  3. Implements the Callable interface

    public class Demo implements Callable { @Override public Object call() throws Exception { return "test"; }}Copy the code
  1. Created from a thread pool

    More on that below!

The first three ways compare:

  • Thread: Inheritance method. It is not recommended to use this method, because Java has single inheritance. If Thread inherits, it cannot inherit other classes
  • Runnable: Implements an interface that is more flexible than Thread, without the restriction of single inheritance
  • Callable: Both Thread and Runnable are overridden run() methods with no return value, Callable is overridden call() methods with a return value and can be used to determine whether a Thread has completed execution or cancel execution with the help of the FutureTask class
  • You use Runnable when the Thread doesn’t need to return a value, and you use Callable when the Thread needs to return a value. Instead of putting the Thread body code directly in the Thread class, you usually start the Thread with the Thread class
  • Thread implements Runnable, Callable encapsulates FutureTask, FutureTask implements RunnableFuture, RunnableFuture inherits Runnable, so Callable is also a Runnable. So all three implementations are essentially Runnable implementations

Thread state

  1. Create (new) state: a multithreaded object is ready, that is, new Thread() is executed; Once the creation is complete, memory needs to be allocated for the thread
  2. Runnable status: The start() method is called, waiting for the CPU to schedule
  3. Running status: Execute the run() method
  4. Blocked status: Temporarily stops the thread from executing, suspends the thread (sleep(), wait(), join(), and the failure to acquire a lock all cause the thread to block), and possibly hands resources over to other threads
  5. Dead state: thread destruction (terminated on normal completion, if an exception occurs, or if interrupt())

Multithreaded approach

Start () and run ()

Start () : starts a thread, requests the CPU for scheduling, and the thread enters the ready state.

Run () : is a method in the thread. Calling this method is just like calling a normal method. It is called in the main thread.

Sleep () and interrupt ()

Sleep () : enter the sleep state, sleep time can be randomly specified, sleep process will give the use of CPU to other threads; But it stays locked during sleep, so it can immediately return to work after waking up.

Interrupt () : Wake up a sleeping thread. This method causes the sleep () method to throw InterruptedException, which interrupts the thread, but does not interrupt as long as you handle the exception, thus waking up the program.

Public class MyThread extends Thread{@override public void run() {system.out.println (" I'm going to sleep "); try { Thread.sleep(100000000); } catch (InterruptedException e) {system.out.println (" Why not sleep "); } system.out.println (" wake up "); } } class Test { public static void main(String[] args) throws InterruptedException { MyThread mt = new MyThread(); mt.start(); for (int i = 0; i < 100; i++) { System.out.println(i); } mt.interrupt(); mt.join(); System.out.println(" ha ha ha "); }}Copy the code
Wait () and notify ()

Wait (): A thread enters the wait block state and waits until it is awakened by another thread using a notify() or notifyAll. The lock is released during sleep. This method can only be called in a synchronous method. If the current thread is not a holder of the lock, the method throws an IllegalMonitorStateException anomalies. Wait (long timeout): Automatically execute when time is up, similar to sleep(long millis) notify(): This method can only be called inside a synchronized method or synchronized block. A thread that called a WAIT method on that object is randomly selected (note: only one is notified) and its blocking state notifyAll(): wakes up all wait objects

Note: The wait() method has false wake up and wakes up where you sleep.

class Share { private int number = 0; public synchronized void decr() throws InterruptedException { if(number ! = 1) { wait(); } number --; System.out.println("test1" + Thread.currentThread().getName()); notifyAll(); } public synchronized void incr() throws InterruptedException { if(number ! = 0) { wait(); // The wait() method has a false wake problem, it will wake up where it sleeps, and when it is awakened by another thread, it will execute at this place, so it should be used with the loop,}else {number ++; System.out.println("test" + Thread.currentThread().getName()); notifyAll(); } } } public class ThreadDemo1 { static Share s = new Share(); public static void main(String[] args) { new ReentrantLock() new Thread(()->{ for(int i = 0; i< 20; i++) { try { s.incr(); } catch (InterruptedException e) { e.printStackTrace(); } } },"aa").start(); new Thread(()->{ for(int i = 0; i< 20; i++) { try { s.decr(); } catch (InterruptedException e) { e.printStackTrace(); } } },"bb").start(); }}Copy the code
The difference between sleep and wait
  • Sleep is in Thread and wait is in Object
  • Sleep does not release the lock, wait does
  • Sleep uses interrupt() to wake up, and wait requires a notify or notifyAll to notify
join()

When the current thread is added to the parent thread, the parent thread is suspended until the joined child thread finishes executing.

class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 100; I ++) {system.out.println (" child thread: "+ I); } } } public class Test { public static void main(String[] args) throws InterruptedException { MyThread mt = new MyThread(); mt.start(); for (int i = 0; i < 100; I ++) {system.out.println (" main thread: "+ I); } mt.join(); System.out.println(" ha ha ha "); }}Copy the code
yield()

Relinquish the CPU, but do not release the lock. Enter ready state and wait for other lines to use first. Unlike sleep (), where sleep () knows when to wake up, yield () determines when to wake up, and is often used to control the speed of a thread’s access.

public class MyThread extends Thread{ public MyThread(String name){ super.setName(name); } @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println(super.getName() + i); if(i%10==0){ Thread.yield(); }}}} class Test {public static void main(String[] args) {MyThread mt = new MyThread("A thread "); MyThread mt1 = new MyThread("B thread "); mt.start(); mt1.start(); }}Copy the code
Priority ()

Set the priority of the thread. A larger number indicates a higher priority. The priority is an integer ranging from 1 to 10. For a finite-level high thread, even though it has a higher priority, it does not get to hog the CPU, it just consumes the CPU for a longer time.

Public class Test {public static void main(String[] args) {MyThread mt1 = new MyThread("A thread "); MyThread mt2 = new MyThread("B thread "); mt1.setPriority(10); Mt2.setpriority (1); mt1.start(); mt2.start(); } } class MyThread extends Thread { public MyThread(String name){ super.setName(name); } @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(super.getName() + "==>"+ i ); }}}Copy the code

\

Thread synchronization

The synchronized keyword

Synchronized’s scope:

1. Synchronized aMethod(){} can prevent multiple threads from accessing the object’s synchronized methods at the same time. No other thread can access any synchronized method on this object simultaneously.

Synchronized static aStaticMethod{} = synchronized static aStaticMethod{} = synchronized static method {} It works on all object instances of a class.

The synchronized keyword can also be used in a block of a method to indicate exclusive access to only that block’s resources. Synchronized (this){/ block /}

Synchronized range:

If applied to normal methods, the current instance object is locked.

If applied to statically synchronized methods, the lock is on the Class object of the current Class.

If applied to a synchronized method block, it locks the object configured in Synchonized parentheses.

Synchronized Indicates the locking principle

The implementation of the synchronized block uses the Monitorenter and monitorexit directives, with the monitorenter indicating the start of the synchronized block and the monitorexit indicating the end of the synchronized block. Upon execution of the start instruction, the thread acquires ownership of the object monitor, which acquires the lock.

If the lock counter is 0, the lock can be acquired. When acquired, set the lock counter to 1, which is the increment of 1.

After monitorexit, set the lock counter to 0 to indicate that the lock is released. If an object lock fails to be acquired, the current thread blocks and waits until the lock is released by another thread.

Synchronized optimization
  1. Biased locking

When a thread, when accessing a synchronized block and obtain the lock will lock in the head and stack frame object record store to lock in the thread ID, after this thread synchronization block on the entry and exit for CAS operation is not needed to lock and unlock, simply test object head Mark in the Word is stored with biased locking points to the current thread.

Biased locking uses a mechanism of waiting until a race occurs to release the lock, so the thread holding the biased lock releases the lock when another thread tries to race the biased lock.

  1. Lightweight lock

(1) Lightweight lock and lock

The JVM will create space for storing lock records in the stack frame of the current thread before executing the synchronization block, and copy Mark Word in the object header to the lock record, officially called swat Mark Word. The thread then tries to use CAS to replace the Mark Word in the object header with a pointer to the lock record. If it succeeds, the current thread obtains the lock; if it fails, other threads compete for the lock and the current thread attempts to spin to acquire the lock.

(2) Lightweight lock unlocking

When lightweight unlocking, CAS operation of atoms will be used to replace the swat Mark Word back to the object header. If successful, it indicates that no competition occurs. If it fails, it indicates that the current lock is in contention and the lock is inflated to a heavyweight lock.

Multi-threaded security issues and solutions

(1) atomicity

Atomicity means that at a moment, only one thread can execute a piece of code that is protected by a Monitor Object. This prevents multiple threads from conflicting when updating the shared state.

(2) Visibility

It must ensure that changes made to shared data before releasing the lock are visible to another thread that subsequently acquires the lock.

(3) Order

The order in which the program is executed is the order in which the code is executed.

Lock solve

As has been said above…

Volatile variable resolution

1. Guarantee visibility, not atomicity

(1) When a volatile variable is written, the JMM forces the variable from the thread’s local memory to main memory;

(2) This write operation invalidates the cache of volatile variables in other threads.

2. Prohibit reordering commands

(1) The reorder operation does not reorder the operations that have data dependencies.

(2) Reordering is to optimize performance, but no matter how it is reordered, the execution result of a single-threaded program cannot be changed

That is, when a volatile variable is executed, all statements preceding it have been executed, but all subsequent statements have not been executed. And the result of the previous statement is visible to the volatile variable and the statements that follow it.

Singleton double locks must be volatile

If volatile is not used, instructions may be rearranged and object creation may fail during multithreading.

Common lock

Spinlock – Spinlock

It is to realize the protection of shared resources and proposed a locking mechanism. In fact, spinlocks are similar to mutexes in that they are designed to solve the problem of mutually exclusive use of a resource. However, the two have slightly different scheduling mechanisms. For mutex, the resource applicant can only go to sleep if the resource is already occupied. But a spin lock does not cause the caller to sleep. If the spin lock is already held by another thread, the caller keeps looping around to see if the holder of the spin lock has released the lock, hence the name “spin”.

Read/write lock – rwlock

Read/write lock is a special type of spin lock. It divides the visitors to a shared resource into readers and writers. Readers only read the shared resource, and writers only write the shared resource

This lock improves concurrency over spin-locks because it allows multiple readers to access a shared resource at the same time on a multiprocessor system, with the maximum possible number of readers being the actual number of logical cpus. A read-write lock can have only one writer or more readers (depending on the number of cpus) at the same time, but it cannot have both readers and writers. — Pessimistic Lock

As the name suggests, it is pessimistic. Every time I try to get the data, I think someone else will change it. So every time I try to get the data, I will lock it, so that other people will block the data until it gets the lock.

Optimistic Lock – Optimistic Lock

As the name implies, it is very optimistic. Every time I go to get the data, I think that others will not modify it, so I will not lock it. However, when I update the data, I will judge whether others have updated the data during this period, and I can use the version number and other mechanisms.

Reentrant lock

Synchronized locks and lock locks are both reentrant locks that lock the same object.

The inner lock does not stop when the outermost lock enters.

The Lock Lock system

In Java multithreading, it is possible to use the synchronized keyword to keep threads from each other, but in JDK1.5, the new ReentrantLock class is added to achieve the same effect and provides more powerful extensions such as sniffer locking, multi-branch notification, and so on. It is also more flexible than synchronized.

Lock Use of a lock

class MyService{ private Lock lock= new ReentrantLock(); public void testMethod() { lock.lock(); for ( int i= 0 ; i< 5 ; i++) { System.out.println( "ThreadName= " +Thread.currentThread().getName()+( " " +(i+ 1 ))); } lock.unlock(); }}Copy the code

Condition implements waiting and notification

Condition can implement multi-notification, that is, multiple instances of Condition (object monitor) can be created within a Lock object, and thread objects can be registered in the specified Condition, which allows for selective thread notification and flexibility in scheduling threads.

class MyService{ private Lock lock= new ReentrantLock(); private Condition condition=lock.newCondition(); public void await() { try { lock.lock(); System.out.println("await time as "+ system.currentTimemillis ()); condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); System.out.println(" lock released "); } } public void signal() { try { lock.lock(); System.out.println("signal time "+ system.currentTimemillis ()); condition.signal(); } finally { lock.unlock(); }}}Copy the code
class MyThread extends Thread{ private MyService service; public MyThread(MyService service) { this.service=service; } @Override public void run() { service.await(); }}Copy the code
public class LockTest { public static void main(String[] args) throws InterruptedException { MyService service=new MyService(); MyThread thread=new MyThread(service); thread.start(); Thread.sleep(3000); service.signal(); }}Copy the code

The wait() method in the Object class corresponds to the await() method in the Condition class. The wait(long Timeout) method in the Object class corresponds to the await(long time,TimeUnit Unit) method in the Condition class. The notify() method in the Object class corresponds to the signal() method in the Condition class. The notifyAll() method in the Object class corresponds to the signalAll() method in the Condition class.

Note: When using the Condition method, you first call the lock.lock() code to get the synchronization monitor

Lock policy:

  • TryLock () : Attempts to lock
  • LockInterruptibly ()
  • TryLock (timeout) : Attempts to lock for a certain timeout period

Types of locks implemented:

  • ReadwriteLock.(Read-write lock)
  • ReentrantLock(ReentrantLock)
  • ReentrantReadWriteLock reentrant read/write lock
  • And users can implement their own locks (such as with spin)

Lock The principle of locking

AQS (AbstractQueueSynchronizer), that is, the abstract queue type synchronizer.

Implementation principle (lock acquisition process) :

  • The dual-end queue holds threads and thread synchronization status.
  • In addition, the CENTRAL Authentication Service (CAS) provides methods for setting the synchronization status

Lock Features of a lock

1) Provide fair lock and non-fair lock: whether to set the synchronization status of threads according to the order of enqueued, that is: whether to set the synchronization status of threads according to the order of enqueued, that is: whether to set the synchronization status of threads according to the order of enqueued, that is: whether to set the synchronization status of threads according to the order of enqueued, that is: whether to set the synchronization status of threads according to the order of enqueued, that is, whether to set the synchronization status of threads according to the time order of enqueued.

2) AQS provides exclusive type, shared type set synchronization state (exclusive lock, shared lock)—— the essence is to set the thread synchronization state

Shared: a number of threads can share the lock. 3) Reentrant: the Reentrant API allows the lock to be acquired multiple times

A deadlock

The lock cannot be released due to mutual waiting. You can’t get the resources you want and you’re permanently suspended.

The reason:

  • System resources are insufficient.
  • The process is running in an inappropriate sequence
  • Misallocation of resources

Verify deadlocks:

  • jps
  • Jstack: A tracing tool that comes with the JVM

The thread pool

Threads are created and destroyed frequently, which has a significant impact on performance. According to the system requirements and hardware environment, the number of threads can be flexibly controlled, and all threads can be unified management and control, so as to improve the system operating efficiency and reduce the system operating pressure

Four common thread pools

CachedThreadPool()

Cacheable thread pool:

  1. Unlimited number of threads
  2. If there are idle threads, idle threads are reused. If there are no idle threads, new threads are created
  3. Certain programs reduce the frequent creation/destruction of threads, reduce overhead

Create method:

 ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
Copy the code

FixedThreadPool()

Fixed length thread pool:

  1. Maximum number of threads concurrently controlled (number of threads executing simultaneously)
  2. Excess threads wait in the queue

Create method:

/ / nThreads = > maximum number of threads that maximumPoolSize ExecutorService fixedThreadPool = Executors. NewFixedThreadPool (int nThreads); / / threadFactory = > create a thread method ExecutorService fixedThreadPool = Executors. NewFixedThreadPool (int nThreads, ThreadFactory threadFactory);Copy the code

ScheduledThreadPool()

Fixed length thread pool:

  1. Supports periodic and periodic task execution.

Create method:

/ / nThreads = > maximum number of threads that maximumPoolSize ExecutorService scheduledThreadPool = Executors. NewScheduledThreadPool (int corePoolSize);Copy the code

SingleThreadExecutor()

Single-threaded thread pools:

  1. One and only one worker thread executes the task
  2. All tasks are executed in the specified order, that is, following the in-queue and out-queue rules of the queue

Create method:

 ExecutorService singleThreadPool = Executors.newSingleThreadPool();
Copy the code

How do I submit a task to a thread pool?

Through the ThreadPoolExecutor. Execute (Runnable command) method to add a thread pool with a task

The strategy of ThreadPoolExecutor
  1. If the number of threads does not reach corePoolSize, a new thread (core thread) is created to execute the task
  1. The number of threads reached corePools, then the task is moved to the queue and waited
  1. The queue is full, and a new thread (not the core thread) executes the task
  1. If the queue is full and the total number of threads reaches maximumPoolSize, an exception will be thrown by RejectedExecutionHandler