Thread Basics

Posted by magic003 on Fri, 14 Jan 2022 17:48:28 +0100

summary

1. Problems solved

  • Blocking operation: keyboard input
  • Time consuming operation: cycle
  • Time slice rotation strategy

2.I/O

  • BIO: blocking
  • NIO
  • AIO: asynchronous operation

Process and thread

1. Introduction

  • System performance bottleneck: long waiting time, fake death, blocking
  • The user inputs an instruction and the computer performs an operation. When the user is thinking or entering data, the computer is waiting for the batch operating system to appear
  • Question 1: if there are two tasks A and B, A needs to read A large amount of data input when A is half executed. At this time, the CPU can only wait quietly for A to read the data before continuing to execute = = = "waste CPU resources in vain
  • Solution 1 + problem 2: multiple programs are installed in memory. When A performs time-consuming operations, let the CPU execute B. then the problem comes. How to distinguish the data used by multiple programs in memory? After A program is suspended, how to restore to the original state when it is restarted?
  • Solution 2: process

2. Process

  • The process corresponds to an application, and each process corresponds to a certain memory address space. It is agreed that each process can only use its own memory space, and each process does not interfere with each other.
  • The running state of the program can be saved in the process space. The CPU polls each process. The next time the process switches back, the original state can be restored from the process space
  • Disadvantages of process: a process can only process one task in a time period
  • Resolution: threads

3. Thread

  • A process contains multiple threads. Each thread executes a subtask. These threads share the resources and memory space of the process
  • Threads implement concurrent execution within a process

4. Differences between processes and threads

  • Process is the basic unit of operating system resource allocation, and thread is the basic unit of operating system scheduling
  • An application corresponds to a process, and a process corresponds to multiple threads
  • Interprocess communication (database) is very troublesome, but threads are very convenient
  • Processes share resources exclusively, and threads share the resources of the processes to which they belong
  • When the process ends, the thread generated by the process will also be destroyed

Thread processing in Java

1.To start a thread start   jvm The in the thread is automatically invoked run()
2.Thread order and CPU The scheduler is about who goes first start irrelevant
3.The main thread code runs, but the sub thread has not run, so the main thread does not exit until the subroutine runs

1. Inherit Thread class

  • **Bottom layer: * * start() - > start0() native C + ± > find run() of MyThread
  • External class
1.External class: MyThread inherit Thread,rewrite run()
2.run()Add time-consuming or blocking operations
3.Disadvantages: Java It is inherited from a single class and cannot inherit from other classes, which affects the extensibility of the class

public class t1_classExtends {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("Main method start");

        MyThread1 m1 = new MyThread1();
        m1.setName("Thread 1--inherit Thread External class--");//Set thread name
        m1.setPriority(1);//Set priority (call the one with higher priority first in theory) 1-10
        m1.start();//Subroutine operation

        for (int i = 0; i < 5; i++) {
            Thread.sleep(1000);
            System.out.println("The main method is running");
        }
    }
}
class MyThread1 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
        	//Thread.currentThread().getName(): outputs the name of the current thread, which is not assigned to the naming system
            System.out.println(Thread.currentThread().getName()+"i=" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Main method start
 Thread 1--inherit Thread External class--i=0
 Thread 1--inherit Thread External class--i=1
 The main method is running
 The main method is running
 Thread 1--inherit Thread External class--i=2
 The main method is running
 Thread 1--inherit Thread External class--i=3
 The main method is running
 Thread 1--inherit Thread External class--i=4
 The main method is running
 Thread 1--inherit Thread External class--i=5
 Thread 1--inherit Thread External class--i=6
 Thread 1--inherit Thread External class--i=7
......
  • Inner class
1.Inner class: when only one class is used
   
public class t1_classExtends {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("Main start method");

        MyThread2 m2 = new MyThread2();
        m2.start();

        for (int i = 0; i < 5; i++) {
            Thread.sleep(1000);
            System.out.println("The main method is running");
        }
    }

    static class MyThread2 extends Thread {
        @Override
        public void run() {
            for (int i = 0; i <= 100; i++) {
                System.out.println("Internal class: i=" + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

2. Implement the operation added to the Runnable interface + run method

  • **Bottom layer: * * start() - > start0() native C + ± > find run() of Thread
if (target != null) {
	target.run();//target is the Runnable object
}
  • External implementation class
1.Disadvantages: the execution result of the new thread cannot be obtained

public class t2_runnable {
    public static void main(String[] args) {
    	System.out.println("Main method start");
    	
 		MyRunnable task = new MyRunnable();
 		
        Thread thread1 = new Thread(task, "Thread 2--realization runnable--");//Create thread object (task, thread name)
        thread1.setPriority(10);
        thread1.start();//After startup, the jvm will automatically call back run() in its configuration task
    }
}

class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            System.out.println(Thread.currentThread().getName()+"i=" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
  • Anonymous Inner Class
1.Anonymous inner class: when used only in one place

public class t2_runnable {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("Main method start");

		//Method 1
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i <= 100; i++) {
                    System.out.println(Thread.currentThread().getName()+"i=" + i);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        },"Thread 3.1--Anonymous Inner Class --");
        thread.start();
        
        //Method 2
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i <= 100; i++) {
                    System.out.println(Thread.currentThread().getName()+"i=" + i);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        },"Thread 3.2--Anonymous Inner Class --").start();
    }
}
  • lambda writing method (functional programming method)
public class t2_runnable {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("Main method start");

		//Method 1
        Thread thread = new Thread( ()->{
			for (int i = 0; i <= 100; i++) {
                System.out.println(Thread.currentThread().getName()+"i=" + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
		},"Thread 4.1--lambda Writing method--");
        thread.start();
        
        //Method 2
        new Thread( ()->{
			for (int i = 0; i <= 100; i++) {
                System.out.println(Thread.currentThread().getName()+"i=" + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
		},"Thread 4.2--lambda Writing method--").start();
    }
}

3. Implement Callable interface

  • Neither of the first two schemes can get the execution result of the new thread
  • Knowledge points contained in the code (binding + get)
/**
 * Implementing the callable interface can have a return value
 */
public class t3_callable {
    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
    	//1.
        FutureTask<Integer> futureTask1 = new FutureTask<Integer>(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int count = 0;
                for (int i = 0; i <= 100; i++) {
                    Thread.sleep(100);
                    count += 1;
                }
                return count;
            }
        });
        
        //2.lambda expression
        FutureTask<Integer> futureTask2 = new FutureTask<Integer>(() -> {
            int count = 0;
            for (int i = 0; i <= 100; i++) {
                Thread.sleep(100);
                count += 1;
            }
            return count;
        });

        //The creation thread is bound to a FutureTask task
        Thread thread = new Thread(futureTask2);//thread(Runnable) polymorphism
        //Start thread
        thread.start();

        //1. Wireless wait (get): jump out when the task execution is abnormal or the task execution is completed
        //Get the return value of the thread and call
        //System.out.println("1+2+...+100=" + futureTask2.get());
        
        //2. Timeout wait get (timeout time, unit): an error is reported when the time is exceeded
        //V get(long timeout, TimeUnit unit)
        System.out.println("1+2+...+100=" + futureTask2.get(1, TimeUnit.SECONDS));
        
        //get is a blocking method. The main thread will not continue until other results come out
        //No, it won't block
        System.out.println("Other codes in the main program");
    }
}

4. Thread pool

  • There is no way to limit the number of threads created directly during the program running. Threads will actually consume space, resulting in memory overflow and virtual machine memory depletion
  • Benefits: reduce the time spent creating and destroying threads and the overhead of system resources, and solve the problem of insufficient resources

/**
 * Thread pool
 */
public class t4_pool {
    public static void main(String[] args) {
        //The size of the core thread pool -- the initial thread size. If it is not enough, it will be expanded to max
        int corePoolSize = 2;
        //Maximum number of threads in the core thread pool
        int maxPoolSize = 4;
        //The maximum idle time of the thread is 10 units. If it is not used, it will be recycled
        long keepAliveTime = 10;
        //Time unit ---- enum constant
        TimeUnit unit = TimeUnit.SECONDS;
        //Blocking queue: the capacity is 2, and two idle tasks are allowed at most
        //This thread allows 6 tasks 4 + 2 at most
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2);
        
        //Thread creation factory
        ThreadFactory threadFactory = new NameThreadFactory();
        //Thread pool rejection policy - there are too many tasks and the blocking queue cannot be saved
        RejectedExecutionHandler handler = new MyIgnorePolicy();
        
        ThreadPoolExecutor executor = null;
        try {
            //The recommended way to create a thread pool does not recommend using an existing API to create a thread pool
            executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
            //Pre start all core threads to improve efficiency
            executor.prestartAllCoreThreads();
            //Number of tasks
            int count = 10;
            for (int i = 0; i <= count; i++) {
                RunnableTask task = new RunnableTask(String.valueOf(i));
                //There are still 4 tasks that cannot be executed when submitting tasks to the thread pool
                executor.submit(task);
            }
        } finally {
            //Assert switchable - EA - Da
            assert executor != null;
            //Close thread pool
            executor.shutdown();
        }
    }

    //Thread factory
    static class NameThreadFactory implements ThreadFactory {
        //AtomicInteger atomic class: a whole = = = concurrent programming thread id
        //Ensure thread safety 123 when a thread uses the thread pool, variables should be added in turn, instead of starting from 0 when a new thread is called
        private final AtomicInteger threadId = new AtomicInteger(1);

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r, "thread --" + threadId.getAndIncrement());//Equivalent to i++
            System.out.println(t.getName() + "Created");
            return t;
        }
    }

    //Thread pool reject policy
    public static class MyIgnorePolicy implements RejectedExecutionHandler {
        @Override                      //Thread pool object rejected
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            doLog(r, executor);
        }

        private void doLog(Runnable runnable, ThreadPoolExecutor e) {
            //Can do logging, etc
            System.out.println("Thread pool:" + e.toString() + runnable.toString() + "Rejected execution");
        }
    }

    //Task class
    static class RunnableTask implements Runnable {
        private String name;

        public RunnableTask(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            try {
                System.out.println(this.toString() + "  is running!");
                Thread.sleep(3000);//Slow down the task
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        @Override
        public String toString() {
            return "RunnableTask{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
}

Thread classification

1. Main thread

  • Thread generated by main method
  • Any Java program has at least one thread

2. Wizard thread (daemon thread)

  • setDaemon(true): sets a thread as a sprite thread
  • When other threads finish running, the wizard thread also ends, which is equivalent to a background thread
  • A Java program is far more than one thread, and there are system startup wizard threads in the background
/**
 * Sprite thread: when other threads end, the sprite also ends
 */
public class t9_deamon {
    public static void main(String[] args) {
        Thread thread1 = new CommonThread();

        Thread thread2 = new Thread(new MyDeamon());

        //Set as daemon thread ----- set the daemon thread before the thread runs
        thread2.setDaemon(true);

        thread2.start();//The original cycle was 20 times, but actually only 5 times
        thread1.start();//Original cycle 5 times
    }
}

//thread 
class CommonThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("User thread:" + i + "Execute!");
            try {
                Thread.sleep(10);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

//Task class
class MyDeamon implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("Daemon thread:" + i + "Execute!");
            try {
                Thread.sleep(10);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

User thread: 0 execution!
Daemon thread: 0 execution!
User thread: 1 execution!
Daemon thread: 1 execution!
User thread: 2 execution!
Daemon thread: 2 execution!
User thread: 3 execution!
Daemon thread: 3 execution!
User thread: 4 execution!
Daemon thread: 4 execution!
Daemon thread: 5 execution!

3. Sub thread

  • Has the same priority as the parent thread

Thread properties

  • id
  • name
  • Priority: priority (1-10), 5 by default
characteristic:
1.Inheritance: child threads and parent threads have the same priority
2.obtain CPU The probability is higher
3.The execution order of threads is not guaranteed, which is not absolute************

Thread group ThreadGroup

1. Function

  • Batch management thread or thread group object

2. Structure

  • First level Association (i.e. only parent-child), multi-level association

3. Use

public class t15_ThreadGroup {
    public static void main(String[] args) {
        TestThread t1 = new TestThread();
        TestThread t2 = new TestThread();

        ThreadGroup g1 = new ThreadGroup("Thread group 1");

        Thread thread1 = new Thread(g1, t1);
        thread1.start();

        Thread thread2 = new Thread(g1, t2);
        thread2.start();

        System.out.println("Active thread group:" + g1.activeCount());
        System.out.println("Thread group name:" + g1.getName());

        //If the thread group is interrupted, all threads in this group are interrupted
        g1.interrupt();
        //The sleep exception in the while loop is caught and the loop terminates
    }
}

class TestThread implements Runnable {
    @Override
    public void run() {
        try {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("Thread Name:" + Thread.currentThread().getName());
                Thread.sleep(3000);//Two threads were interrupted
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Throw two exceptions

Thread interrupt

1. General

  • Thread interrupt is a cooperative mechanism between threads
  • The interrupt will not make the thread quit running immediately. The interrupted thread should choose a reasonable processing method according to the interrupt state
  • Interrupt is only an identification bit attribute. It only notifies rather than forces the thread to exit. If a thread interrupt is detected and not processed, the thread can still continue to execute

2. Response to thread interruption

  • Throw InterruptedException
  • Catch the InterruptedException and throw it again
public class t7_ReThrowInterruptException {
    public static void main(String[] args) throws InterruptedException {
        //Current thread
        Thread thread = Thread.currentThread();
        try {
            //Detects whether the current thread is interrupted
            thread.interrupt();
            //Thread sleep 3s
            Thread.sleep(3000);//Unable to sleep after interruption
        } catch (InterruptedException e) {
            System.out.println(thread.getName() + "Throw InterruptedException Interrupt exception");
            System.out.println(thread.getName() + "Do some cleaning");
            throw e;
        }
    }
}
  • When an interrupt is detected, reset the thread interrupt
public class t8_ReInterrupted extends Thread {
    public static void main(String[] args) throws InterruptedException {
        //Current thread main
        String threadName = Thread.currentThread().getName();

        //Create thread
        t8_ReInterrupted reInterrupted = new t8_ReInterrupted();
        System.out.println(printDate() + threadName + "Thread start");

        //Start a new thread
        reInterrupted.start();

        //Main thread sleep 3s
        Thread.sleep(3000);
        System.out.println(printDate() + threadName + "Set child thread interrupt");

        //Set thread interrupt for new thread
        reInterrupted.interrupt();//Sleep 1s interrupted
        //Main thread sleep 3s
        Thread.sleep(3000);
        System.out.println(printDate() + threadName + "End of operation");
    }

    @Override
    public void run() {
        //Current thread
        String threadName = Thread.currentThread().getName();
        int i = 0;
        //The for loop waits for a thread interrupt. If the current thread is not in the interrupt state, it will continue to be an interrupt and exit the current thread
        while (!Thread.currentThread().isInterrupted()) {
            System.out.println(printDate() + threadName + "The thread is executing the second " + (++i) + "second");
            try {
                //It will be executed 3 times
                //Thread blocking, if the thread receives an interrupt operation signal, it will throw an exception
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println(printDate() + threadName + "Thread executing");
                //Detect whether the thread is interrupted
                System.out.println(printDate() + threadName + "Status of:" + this.isInterrupted());//false
                //If you need to maintain the interrupt state, you need to reset the interrupt state
                //If TODO is not needed, do not call interrupt(). If interrupt() is called, the status of the current thread will change to interrupt while, the loop will exit, and the program will end
                Thread.currentThread().interrupt();//true
            }
        }
        System.out.println(printDate() + threadName + "Whether the thread is interrupted:" + this.isInterrupted());
        System.out.println(printDate() + threadName + "Thread exit");
    }

    private static String printDate() {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        return simpleDateFormat.format(new Date()) + " ";
    }
}

3. Thread interrupt operation

  • Thread calls interrupt() to interrupt
  • Detect whether this thread is interrupted through isInterrupted(), but the interrupt state will not be cleared
public class t6_isInterrupted {
    public static void main(String[] args) {
        //Current thread
        Thread thread = Thread.currentThread();
        //Detects whether the current thread is interrupted
        System.out.println(thread.getName() + "Whether the thread is interrupted:" + thread.isInterrupted());//false
        
        thread.interrupt();

        //Detects whether the current thread is interrupted
        System.out.println(thread.getName() + "Whether the thread is interrupted:" + thread.isInterrupted());//true
        //Detects whether the thread interrupt status is cleared
        System.out.println(thread.getName() + "Whether the thread is interrupted:" + thread.isInterrupted());//true
	}
}
  • interrupted() can also detect the interrupt state and clear the interrupt state at the same time
public class t5_interrupt {
    public static void main(String[] args) {
        //Interrupt is implemented through interrupt() method
        //Detecting whether the thread is interrupted by the interrupted() method will clear the interrupt state

        //Thread.currentThread() main thread
        System.out.println(Thread.currentThread().getName() + "Whether the thread is interrupted:" + Thread.interrupted());//false

        //Set thread interrupt to directly implement interrupt
        Thread.currentThread().interrupt();//Become interrupted

        //Thread.currentThread().stop();// Expiration (forcing threads to shut down can be unsafe)

        //Restore status after returning true
        System.out.println(Thread.currentThread().getName() + "Whether the thread is interrupted:" + Thread.interrupted());//true

        System.out.println(Thread.currentThread().getName() + "Whether the thread is interrupted:" + Thread.interrupted());//false
    }
}

Thread life cycle

1. Functional programming

  • There can only be one abstract method in a functional interface (excluding methods with the same name as Object methods and abstract methods inherited from Object)
  • The naming of the only abstract method in the interface is not important. Lambda expressions are supported
  • Writing method (understanding)************
@FunctionalInterface
//Functional interface annotation: supports functional operations
interface Action<T> {
    void execute(T t);//An abstract method
}

public class t11_function{
    public static void main(String[] args) {
        //1
        Action action = System.out::println;//Implementation of execute as an abstract method in functional interface
        action.execute("hello");

        //2. Callback method
        test(System.out::println, "hello");

		//3. The new parameterless and return value free method replaces the only abstract function of the functional interface
		//	Equivalent to sleepMethod instead of run
		t11_function t1 = new t11_function();
		new Thread(t1::sleepMethod).start();
    }

    static void test(Action action, String str) {
        action.execute(str);
    }

	public synchronized void sleepMethod() {
        ....
    }
}

2. Using life cycle function

  • synchronized keyword
Category: 1.Sync code block: small range
		synchronized(Locked object){}---The locked object refers to this
	  2.Synchronization method: wide range
 Object lock: one lock per object
 Function: solve the problem of inconsistent data when multithreading operates a resource(Resource competition, competition condition)


  • Sleep: thread Sleep (time) / / MS
characteristic
	1.Static method: Thread.sleep(time)
	2.There may be exceptions in the process: try{}catch(){}
		InterruptedException(Tested abnormality)
			review:1.Non inspected exception: RuntimeException And subclasses
				 2.Detected exception: it is required to be forcibly caught or thrown in the program
	3.sleep After that, the thread enters the timeout waiting state. When the time expires, it changes to the ready state and is scheduled to CPU Run, otherwise it is ready
	4.interrupt(): Interrupt the sleep state of the thread. After interruption catch It will work
	5.sleep Only temporarily relinquish the execution right without releasing the lock
	**********Because the lock is not released(object monitor ),Therefore, other threads are still unable to obtain object locks
	6.It is only a thread operation and does not involve inter thread communication(Do not release lock)

sleep and wait The difference between methods on lock( synchronized)
	1.sleep(): CPU If it is not used, it gives up the execution right, but does not release the lock, so the threads execute in sequence
		When a thread comes, the next one will be executed only after a thread is executed
		Poor performance
	2.wait(): CPU If it is not used, give up the execution right and release the lock at the same time
		Alternate execution between threads, wait Then execute other threads first, and continue to execute when the lock is obtained again
  • yield
yield Make the current thread yield cpu But it does not guarantee that other threads can obtain the right to use CPU Executive power (meaning that without executive power, you may need to execute yourself first)
	Don't run first

yield And sleep Differences between
	1. sleep Give other threads a chance to run, but do not consider the priority of other threads; but yield It will only give way to threads with the same or higher priority;
	2. sleep Abnormal(Can interrupt), yield No,(Can't interrupt)
	3. When the thread executes sleep Method, it will go to the timeout waiting state,
	   Yes yield Method, go to the ready state;
  • join
join Causes one thread to execute after the end of another thread. 
	In a thread(such as main)Calling another thread in join(),Then the current thread(main)Blocking, allowing another thread to execute before the current thread executes
	
yield And join Differences between
	1. yield Is a static method, join Is an instance method(thread object.join())
	2. yield It will only give way to threads with the same or higher priority, join Priority independent
  • wait
1.Object Method of
2.Parameters: wait()
	    wait(long timeout)
	    wait(long timeout,nanos): (Timeout in)
3.To execute wait(),You need a lock first, so wait()Must again synchronized in
  • nitify
1.Object Method of
2.Wake up a thread
  • notifyAll
1.Object Method of
2.Wake up the thread in the waiting queue

deadlock

1. General

  • A blocking phenomenon caused by two or more processes competing for resources or communicating with each other in the execution process. If there is no external force, they will not be able to move forward

2. Four conditions for deadlock generation

  • Mutex condition: a resource can only be used by one process at a time
    =====Object lock: only one thread can hold it at a time

  • Request and hold condition: when a process is blocked by requesting resources, it will hold the obtained resources

  • Conditions of non deprivation: the resources obtained by the process cannot be forcibly deprived until they are used up at the end of the year

  • Circular waiting condition: a circular waiting resource relationship is formed between several processes

3. Solutions

  • Find deadlock position dump
  • Conditions for arbitrarily breaking a deadlock

4. Cases

/**
 * deadlock
 */
public class t17_deadLock implements Runnable {
    public int flag = 1;

    static Object o1 = new Object();
    static Object o2 = new Object();

    public static void main(String[] args) {
        //Creating two objects produces two object locks
        t17_deadLock td1 = new t17_deadLock();
        t17_deadLock td2 = new t17_deadLock();

        td1.flag = 1;
        td2.flag = 0;
        Thread t1 = new Thread(td1);
        Thread t2 = new Thread(td2);
        t1.start();
        t2.start();
    }

    /**
     * 1.Mutex: synchronized
     * 2.Request and hold: acquire o1 will always lock
     * 3.Inalienable: when flag=1, o1 is obtained, and flag=0 cannot be forcibly deprived
     * 4.Loop wait: the relationship between two threads is a loop
     */
    @Override
    public void run() {
        System.out.println("flag=" + flag);
        if (flag == 1) {
            synchronized (o1) {//o1 locking
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o2) {//o1 to obtain o2 lock
                    System.out.println("1");
                }
            }
        }
        if (flag == 0) {
            synchronized (o2) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o1) {
                    System.out.println("2");
                }
            }
        }
        //Breaking a condition resolves the deadlock
        //Both take o1 first and then o2
    }
}

Producer and Consumer

/**
 * bounded-buffer problem 
 */
public class productConsumer {
    public static void main(String[] args) {
        AppleBox ab = new AppleBox();//Create Middleware

        //Note that ab is the same ab, so the pc uses the same lock
        Producer p = new Producer(ab);
        Consumer c = new Consumer(ab);
        //Consumer cd = new Consumer(ab);

        new Thread(p).start();
        new Thread(c).start();

        //new Thread(cd).start();
    }
}

//Pending messages
class Apple {
    int id;

    Apple(int id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "Apple" + id;
    }
}

//middleware 
class AppleBox {
    int index = 0;
    Apple[] apples = new Apple[5];

    public synchronized void deposite(Apple apple) {//Only one producer can come in
        //It's full
        while (index == apples.length) {//Do not if wake up and judge again to prevent the index from crossing the boundary = = = multiple fault tolerance
            try {
                this.wait();//It will not end until you sleep when you are full, and wait until you wake up with time, so you don't need to be activated by others
                //But the time of waking up is unknown. It's better to inform others
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.notifyAll();//Notify other threads to activate. Even if they are all alive, only those with locks can run
        apples[index] = apple;
        index++;
    }

    public synchronized Apple withdraw() {//Only one consumer can come in
        //The container is empty
        while (index == 0) {//Don't if wake up and judge again to prevent the index from crossing the line
            try {
                this.wait();//Release lock
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.notifyAll();//Wake up all waiting states in ab lock to continue
        index--;
        return apples[index];
    }
}

class Producer implements Runnable {
    AppleBox ab = null;//The resources corresponding to production and consumption are the same

    Producer(AppleBox ab) {
        this.ab = ab;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            Apple a = new Apple(i);
            ab.deposite(a);
            System.out.println(Thread.currentThread().getName() + "Produced" + a);
            try {
                Thread.sleep((int) (Math.random() * 1000));//Production takes a little time
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Consumer implements Runnable {
    AppleBox ab = null;//The resources corresponding to production and consumption are the same

    Consumer(AppleBox ab) {
        this.ab = ab;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            Apple a = ab.withdraw();
            System.out.println(Thread.currentThread().getName() + "Consumption" + a);
            try {
                Thread.sleep((int) (Math.random() * 1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Supplementary - interview often ask volatile

1.volatile

  • The most lightweight synchronization mechanism provided by java virtual machine
  • Modifier variable, but it cannot be a local variable and cannot be in the method body
  • Function: ensure the visibility of shared variables among multiple threads, and ensure that the values of shared variables read by all threads at the same time are consistent. If a thread updates the shared variable modified by volatile, other threads can immediately see the updated shared variable value of the thread
  • Disadvantages: the atomicity of shared variables and thread safety cannot be guaranteed
  • Ensure visibility and effectiveness, but not atomicity

2. Memory model of modern computer

  • Instructions: executed by CPU processor
  • Data: stored in main memory
Question 1:
	There is an order of magnitude gap between the computing speed of the computer's storage device and the processor
 Solution 1:
	Modern computer systems add a layer of cache whose read-write speed is close to that of the processor(Cache),As a buffer between memory and processor.(after CPU register),It avoids reading data from main memory every time
  • Cache: L1 cache, L2 cache, L3 cache (multi-core sharing)
  • Computer Abstract memory model

  • Supplement: Bus

3. Cache consistency

  • When the operation tasks of multiple processors involve the same main memory area, the cache data may be inconsistent
  • solve
Method 1.Bus+lock
	Add monitoring on the bus, and when it is occupied, other CPU You can't operate
	While locking the bus, other CPU Is unable to access memory, so the efficiency is relatively low
 Method 2.Snooping Technology+Cache consistency protocol
	Snooping Technology: -- guarantee CPU I know the data has changed
			All data transmission takes place on a shared bus. Each processor checks whether its cache value has expired by sniffing the data propagated on the bus
			If the processor finds that the memory address corresponding to its cache line has been modified, it will set the cache line of the current processor to an invalid state
			When the processor modifies the data, it will re read the data from the system memory to the processor cache
	Cache consistency protocol: how to handle different data
			Each processor follows some protocols when accessing the cache, and operates according to the protocols when reading and writing
			Agreement has MSI,MESI(IllinoisProtocol),MOSI,Synapse,Firefly and DragonProtocol etc.

			with MESI Protocol (protocol supporting write back cache)) take as an example
				core:When CPU When writing data, if it is found that the variable of the operation is a shared variable, it is found in other variables CPU A copy of the variable also exists in
					Signals other CPU Set the cache line of the variable to an invalid state
					So when others CPU When you need to read this variable, you find that the cache line that caches this variable in your cache is invalid
					Then it will re read from memory
				stay MESI Under the agreement, CPU Four states of each cache row tag in the

4. Instruction reordering problem

Reordering level
	1.Compiler optimized reordering
	2.Reordering of instruction level parallelism,Completed by processor
		If there is no data dependency, the processor can change the execution order of the machine instructions corresponding to the statements
	3.Reordering of memory systems

  • The written code is inconsistent with the execution order in the CPU. This is to improve the execution efficiency of the CPU, which may lead to inconsistent results of multiprocessors running the same piece of code (single thread ensures consistency)

  • solve

Memory barrier: hardware layer technology
	type: Load Barrier and Store Barrier Read barrier and write barrier.
	effect:
		1.Prevent reordering of instructions on both sides of the barrier
		2.Force write buffer/The dirty data in the cache is written back to the main memory to invalidate the corresponding data in the cache

5. Different hardware provides different memory barriers

  • java is cross platform, and java programs will eventually run on these platforms, and the memory barriers provided by different platforms are inconsistent
  • solve
java Shield these differences through jvm Instructions for generating memory barriers

6.JMM (Java memory model)

  • The Java memory model is analogous to the computer memory model
  • Function: shield the memory access differences of various hardware and operating systems, so that Java programs can achieve consistent memory access effects on various platforms
  • For better execution performance, the JAVA memory model does not restrict the execution engine to use the processor's specific registers or cache to deal with the main memory, nor does it restrict the compiler to adjust the code order optimization. Therefore, the JAVA memory model will have cache consistency problems and instruction reordering problems
  • The Java Memory Model stipulates that all variables (instance variables and static variables, excluding local variables) are stored in the main memory (equivalent to the physical memory of the computer), and each thread (because the thread runs in the jvm, and the jvm can be understood as a virtual system) has its own working memory (similar to the cache of the computer model),
    The working memory of a thread stores the main memory copy of the variables used by the thread. All operations of a thread on variables must be carried out in the working memory, and the main memory cannot be operated directly, and each thread cannot access the working memory of other threads

7. Three features of concurrent programming

  • Atomicity
Operations of basic types no larger than 32 bits are atomic
 about long and double Variables, treat them as two atomic 32-bit values instead of an atomic 64 bit value, 
	Will one long When the value of type is saved to memory, it may be two 32-bit writes
	2 When a competing thread wants to write different values to memory, the values in memory may be incorrect

1.i=666  Atomicity
	Write the value 666 directly to the working memory
2.i=j  Non atomicity
	In fact, it involves two operations: read first j Value, and then j The value of is written to working memory
	Both operations satisfy atomicity separately
3.i=i+1  Non atomicity
4.i++  Non atomicity
  • (result) visibility
When one thread changes the value of a shared variable, other threads can immediately know the change
JMM By synchronizing the new value back to main memory after variable modification, the variable value is refreshed from main memory before variable reading
	This method relies on main memory as the transmission medium to achieve visibility, whether it is ordinary variables or volatile This is true for all variables
	
volatile: Ensure that the new value can be synchronized back to main memory immediately and refreshed from main memory immediately before each use, so we say volatile It ensures the visibility of multi-threaded operation variables without atomicity
synchronized and Lock It can also ensure visibility. Before releasing the lock, the thread will brush the shared variable values back to main memory to ensure atomicity
final Visibility can also be achieved, which cannot be modified directly
  • Order
JMM definition:If observed within this thread, all operations are orderly(as-if-serial)If you observe another thread in one thread, all operations are out of order. 
	The latter half of the sentence means to allow the compiler and processor to reorder instructions, It will affect the correctness of multithreading concurrent execution
	And the first half of the sentence means as-if-serial That is, no matter how reordering (compiler and processor to improve parallelism) the execution result of the program will not be changed

8. By default, how does the jvm ensure the order of compilation instructions (without any keywords) = = = development first principle (JSR 133 specification)

  • Happens before principle
 - Program Order Rule 
	In a thread, according to the order of control flow, the operation written in front of it first occurs in the operation written in the back
 - Tube side locking rules
	One unLock The operation occurs first and then on the same lock lock operation
 - volatile Variable rule
	Yes, one volatile The write operation of a variable occurs first in the subsequent read operation of the variable
 - Thread start rule
	Thread Object start()Method occurs first in each action of this thread
 - Thread termination rule
	All operations in the thread occur first in the thread termination detection. We can Thread.join()Method end Thread.isAlive()The return value means of detected that the thread has terminated execution
 - Thread interrupt rule
	To thread interrupt()The code of the interrupted thread first detects the occurrence of the interrupt event
 - Object termination rule
	The initialization of an object is completed first finalize()Start of method
8.Transitive rule
	If operation A Occurs prior to the operation B,And operation B It occurs in operation first C,Then the operation can be obtained A Occurs prior to the operation C
  • analysis
volatile bool flag = false;
int b = 0;

public void read() {
   b = 1;              //1
   flag = true;        //2
}

public void add() {
   if (flag) {         //3
       int sum =b+b;   //4
       System.out.println("bb sum is"+sum); 
   } 
}
Press happens-before Principle analysis: 
	1.flag add volatile Keyword, which prohibits instruction rearrangement, that is, 1 happens-before 2 Yes
	2.according to volatile Variable rule( volatile Variables are written before read),2 happens-before 3
	3.From the program order rules, 3 happens-before 4
	4.By transitivity, 1 -> 2  -> 3  -> 4   ,   Get 1 happens-before 4,Therefore, proper output sum=2

9.volatile bottom layer

  • How does the underlying volatile ensure visibility



  • In fact, volatile guarantees visibility and prevents instruction rearrangement are related to memory barriers
Singleton mode of double check lock
public class Singleton {  
    private volatile static Singleton instance;  
    private Singleton (){}  
    public static Singleton getInstance() {  
	    if (instance == null) {  
	        synchronized (Singleton.class) {  
		        if (instance == null) {  
		            instance = new Singleton();  
		        }  
	        }  
	    }  
	    return instance;  
    }  
}  

Observation has volatile Keywords and no volatile Keywords instance Generated assembly code discovery
	have volatile When a keyword is modified, there will be one more lock addl $0x0,(%esp)
		That is, one more lock Prefix instruction
lock The instruction is equivalent to a memory barrier, which ensures the following:
	1.During reordering, subsequent instructions cannot be reordered to the position before the memory barrier
	2.Write the cache of this processor to memory 
	3.If it is a write action, the corresponding cache in other processors will be invalid
 Summary:Points 2 and 3 are not volatile To ensure visibility, the first point is to prohibit instruction rearrangement
  • volatile underlying memory barrier
In order to achieve volatile Memory semantics, Java The memory model adopts the following conservative strategies
	1. In each volatile Insert a before the write operation StoreStore a barrier
	2. In each volatile Insert a after the write operation StoreLoad a barrier
	3. In each volatile Insert a before the read operation LoadLoad a barrier
	4. In each volatile Insert a after the read operation LoadStore a barrier

The memory barrier ensures that the previous instructions are executed first, so this ensures that instruction rearrangement is prohibited
 At the same time, the memory barrier ensures that the cache is written to memory and other processor caches fail, which ensures visibility

8. Take a chestnut

int i=0;
i=i+1;
The above runs in single threaded and multi-threaded environments.
  • Single thread
  • Multithreading

Interview - synchronized

1. General

  • Exclusive pessimistic lock (pessimistic that there must be thread contention, forced lock)
  • Reentrant lock (optimization strategy for multiple threads. The same thread can obtain the same lock multiple times without cutting the context)
  • Heavyweight lock (large amount of information)
  • Mutex (only one allowed at a time)
  • Scope of action
1.Object locks: member variables and non static methods, instances of objects
	synchronized(obj){}
	public synchronized void add(){}
2.Class lock: static method, locked Class example
	obtain Person Object lock of reflective object of class 
	synchronized(Person.class){}

2. Internal structure

  • Storage structure of classes in JVM
  • Structure of MarkWord
  • Storage structure of classes in JVM
Usually say Synchronized Object lock, MarkWord The lock identification bit is 10, where the pointer points to Monitor The starting address of the object
 stay Java Virtual machine( HotSpot)In, Monitor By ObjectMonitor The main data structures are as follows (located at HotSpot Virtual machine source code ObjectMonitor.hpp Documents, C++Implemented)

ObjectMonitor() {
   _header       = NULL;
   _count        = 0; // Number of records
   _waiters      = 0,
   _recursions   = 0;
   _object       = NULL;
   _owner        = NULL;
   _WaitSet      = NULL; // The thread in wait state will be added to_ WaitSet
   _WaitSetLock  = 0 ;
   _Responsible  = NULL ;
   _succ         = NULL ;
   _cxq          = NULL ;
   FreeNext      = NULL ;
   _EntryList    = NULL ; // Threads in the state of waiting for lock block will be added to the list
   _SpinFreq     = 0 ;
   _SpinClock    = 0 ;
   OwnerIsThread = 0 ;
   }

_count Used to record the number of times the thread acquired a lock
_WaitSet Store in wait Thread queue with status
_EntryList The storage is waiting for a lock to be acquired block Thread queue of status, that is, blocked threads
_owner Point to holding ObjectMonitor Object's thread
  • Lock expansion process

3. Implementation principle

  • In order to reduce the performance consumption caused by obtaining and releasing locks, Java SE 1.6 introduces "biased locks" and "lightweight locks"
  • There are four lock states, from low to high: no lock state, biased lock state, lightweight lock state and heavyweight lock state
  • Lock inflation: locks can be upgraded but not demoted
  • schematic diagram
  • analysis
When multiple threads access a piece of synchronous code at the same time, they will enter the_EntryList In the queue,
When a thread gets an object's monitor Post entry_Owner Area and put monitor Medium_owner Variable is set to the current thread,
meanwhile monitor Counters in_count Plus 1,
If thread calls wait()Method to release the currently held monitor,
_owner Restore variable to null,_count Subtract 1 and the thread enters_WaitSet Waiting to be awakened in the collection.
If the current thread finishes executing, it will also be released monitor(lock)And reset the value of the variable,
So that other threads can get in monitor(lock)

Supplement - ThreadLocal class is often asked in interviews

  • jdk1. Thread member operation class provided from 2
  • Requirement scenario: the execution of the same Runnable can be distinguished according to different threads
  • case
public class Test1_original{
	public static void main(String[] args) {
		final int arg=0;
		Thread t1=new Thread( new Runnable() {
			public void run() {
				task1( arg );
			}
		});
		t1.start();
	}
	
	public static void task1(  int arg) {
		task2( arg);  
		//If parameters are used in subsequent methods, they need to be passed continuously. If they are not passed, arg will be lost. If there are multiple parameters, each parameter must be passed, and the code is redundant
	}
	public static void task2( int arg) {
		
	}
}
  • Solution
public class Test2 {
	static ThreadLocal<Integer> arg = new ThreadLocal<>();

	public static void main(String[] args) {

		Thread t1 = new Thread(new Runnable() {
			public void run() {
				// Initialization parameters
				arg.set(0);
				// Parameters do not need to be passed again
				task1();
			}
		});
		t1.start();

		Thread t2 = new Thread(new Runnable() {
			public void run() {
				// Initialization parameters
				arg.set(1);
				// Parameters do not need to be passed again
				task1();
			}
		});
		t2.start();

	}

	public static void task1() {
		task2();
	}

	public static void task2() {
		System.out.println(arg.get());
	}
}
  • analysis
1.As long as you can access ThreadLocal Variable, you can get the specified value
2.It and static Differences between?
	ThreadLocal#The value obtained by get is unique to each thread 
  • ThreadLocal source code analysis
1. set method
	public void set(T value) {
	      //Get current thread
	      Thread t = Thread.currentThread();
	      //Type of data structure actually stored
	      ThreadLocalMap map = getMap(t);
	      //If there is a map, set it directly. If there is no map, create a map and set it
	      if (map != null)
	          map.set(this, value);
	      else
	          createMap(t, value);
	 }
2. get method
	ThreadLocalMap getMap(Thread t) {
		//A ThreadLocalMap is maintained in thred
		return t.threadLocals;
	}
3. Create method
	void createMap(Thread t, T firstValue) {
	      //Instantiate a new ThreadLocalMap and assign it to the thread's member variable threadLocals
	      t.threadLocals = new ThreadLocalMap(this, firstValue);
	}
4. ThreadLocalMap
	//Entry is a static internal class of ThreadLocalMap. If it refers to ThreadLocal
	//At the same time, ThreadLocal and stored value form a key value relationship
	static class Entry extends WeakReference<ThreadLocal<?>> {
	    /** The value associated with this ThreadLocal. */
	    Object value;
	
	    Entry(ThreadLocal<?> k, Object v) {
	           super(k);
	            value = v;
	    }
	}

	//ThreadLocalMap construction method
	ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
	        //Internal member array, initial_ Constant with capability value of 16
	        table = new Entry[INITIAL_CAPACITY];
	        //Bit operation, the result is the same as that of modulo, and the location to be stored is calculated
	        //threadLocalHashCode is interesting
	        int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
	        table[i] = new Entry(firstKey, firstValue);
	        size = 1;
	        setThreshold(INITIAL_CAPACITY);
	}

WeakReference And one Map,These two places need to be understood. These two things are a.Java Weak reference, that is GC The reference package will be destroyed when(quote)Object, this threadLocal As key May be destroyed, but as long as we define his class as not uninstalled, tl This strong reference always refers to this ThreadLocal Yes, it will never be gc fall
					In instantiation ThreadLocalMap A length of 16 is created Entry Array. adopt hashCode And length Bit operation determines an index value i,this i Is stored in table Position in array. So each thread Thread All hold one Entry Array of type table,All the reading process is through the operation of this array table Completed
					The following code
						//Three types of ThreadLocal of ABC are declared in a thread
ThreadLocal<A> sThreadLocalA = new ThreadLocal<A>();
ThreadLocal<B> sThreadLocalB = new ThreadLocal<B>();
ThreadLocal<C> sThreadLocalC = new ThreadLocal<C>();
						For a Thread There is only one ThreadLocalMap,therefore ABC Corresponding to the same ThreadLocalMap Object. In order to manage ABC,So they are stored in different locations in an array, and this array is mentioned above Entry Array of type table. 

So here comes the question..., ABC stay table How is the position in the determined? In order to normally access the corresponding value, there must be a method to calculate the determined index value i, This is it.  set Function of method. 

  //set method in ThreadLocalMap.
  private void set(ThreadLocal<?> key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            Entry[] tab = table;
            int len = tab.length;
            //Get the index value, which is a special place
            int i = key.threadLocalHashCode & (len-1);

            //Traverse the tab. If it already exists, update the value
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }
            
            //If the above traversal is not successful, a new value is created
            tab[i] = new Entry(key, value);
            int sz = ++size;
            //Expansion of qualified array x2
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }
						Explain again   
							int i = key.threadLocalHashCode & (len-1)
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1)

take threadLocalHashCode Perform a bit operation (modulo) to get the index i,
							What's in this  key.threadLocalHashCode How did you get it. 
								 //Related codes of threadLocalHashCode in ThreadLocal
    
    private final int threadLocalHashCode = nextHashCode();

    /**
     * The next hash code to be given out. Updated atomically. Starts at
     * zero.
     */
    private static AtomicInteger nextHashCode =
        new AtomicInteger();

    /**
     * The difference between successively generated hash codes - turns
     * implicit sequential thread-local IDs into near-optimally spread
     * multiplicative hash values for power-of-two-sized tables.
     */
    private static final int HASH_INCREMENT = 0x61c88647;

    /**
     * Returns the next hash code.
     */
    private static int nextHashCode() {
        //Self increasing
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }
								because static The reason is that every time new ThreadLocal Time because threadLocalHashCode Initialization of will cause threadLocalHashCode The value is incremented once by 0 x61c88647
								0x61c88647 Is the Fibonacci hash multiplier,Its advantage is to hash through it(hash)The results will be evenly distributed and can be avoided to a great extent hash conflict
 Summary
	For a ThreadLocal In other words, his index value i Yes, when accessed between different threads, the access is different table The same position of the array is table[i],It's just between different threads table Is independent.
	Different for the same thread ThreadLocal In general, these ThreadLocal Share an instance table Array, and then each ThreadLocal Instance in table Index in i It's different.
//get method in ThreadLocal
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}
    
//getEntry method in ThreadLocalMap
private Entry getEntry(ThreadLocal<?> key) {
       int i = key.threadLocalHashCode & (table.length - 1);
       Entry e = table[i];
       if (e != null && e.get() == key)
            return e;
       else
            return getEntryAfterMiss(key, i, e);
   }
	
		

Understanding of ThreadLocal and Synchronized

identical:It is to solve the access conflict of the same variable in multiple threads

Different:1. Synchronized It is to solve the access conflict by thread waiting and sacrificing time
	 2. ThreadLocal It is to solve the conflict by sacrificing a separate storage space for each thread, and compared with Synchronized,ThreadLocal It has the effect of thread isolation. The corresponding value can be obtained only within the thread, and the desired value cannot be accessed outside the thread
	
Application scenario:When some data is thread scoped and different threads have different data copies, it can be considered ThreadLocal

Topics: Java Big Data Back-end