Catalog
Multithreaded
Knowledge Points
Create Threads
There are three ways to create threads in Java
(1) Inherit the Thread class and override the run method
The run method in the Thead class is not an abstract method, nor is the Thread class an abstract class
myThread is a separate thread when it inherits the Thread class
To have the thread start. Invoke thread's Start method
When the start method is called to start a thread, the code of the overridden run method is executed
class MyThread extends Thread { @Override public void run() { System.out.println(2); } } public class Ch01 { public static void main(String[] args) { System.out.println(1); MyThread myThread = new MyThread(); myThread.start(); // Common object tuning methods // myThread.run(); // Thread priority, probability problem! Can't do 100 percent // 90 will run first Main Method 10 will run mythread first System.out.println(3); System.out.println(4); } }
The call is start and the execution is run. Why not call run directly?
If you adjust the run() method directly, it is no different from the object method
(2) Implement Runnable interface
@FunctionalInterface: Functional interface, you can use arrow functions
If you want the thread to start, you must call the start method in the Thread class
Question: Can't find the start() method after implementing the Runnable interface?
class MyThread2 implements Runnable { @Override public void run() { System.out.println(2); } } public class Ch02 { public static void main(String[] args) { System.out.println(1); // start MyThread2 myThread2 = new MyThread2(); // If you want the thread to start, you must call the start method in the Thread class // Question: The Runnable interface has been implemented, and the start method cannot be found? Thread t = new Thread(myThread2); t.start(); System.out.println(3); System.out.println(4); } }
Using the arrow function (lambda) function
Arrow function interface, Abstract class, override method
public class Ch03 { public static void main(String[] args) { System.out.println(1); // Arrow function interface, Abstract class, override method new Thread(() -> System.out.println(2)).start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(3); System.out.println(4); } }
(3) Implement callable interface
callable--->FutureTask-->RunnableFuture-->Runnable-->Thread
class MyThread3 implements Callable<String> { @Override public String call() throws Exception { System.out.println(2); return "call Return value of method"; } } public class Ch04 { public static void main(String[] args) { System.out.println(1); // Callable-->FutureTask-->RunnableFuture-->Runnable-->Thread FutureTask<String> futureTask = new FutureTask<>(new MyThread3()); new Thread(futureTask).start(); System.out.println(3); System.out.println(4); } }
thread priority
Probability problems, less than 100%, 90% will run the main method first, 10% will run myThread first
Daemon Threads
There are two types of threads available in Java:
1. User Threads
2. Daemon Threads
Daemon threads serve user threads only when they are running)
Daemon threads are useful for background support tasks
Garbage collection, most JVM threads are Daemons
QQ:
The main program is the user thread
Any thread inherits the state of the thread daemon that created it, and since the main thread is the user thread, any thread started within the main method is the daemon by default.
public class Ch05 extends Thread { @Override public void run() { super.run(); } public static void main(String[] args) { Ch05 ch05 = new Ch05(); // ch05 becomes a daemon thread ch05.setDaemon(true); ch05.start(); } }
Thread life cycle
cradle to grave
NEW: This state is mainly due to the thread not being executed by the start() call
RUNNABLE: Thread is executing in JVM, waiting for dispatch from operating system
BLOCK: Blocked because for some reason immediate execution is not possible and pending wait is required
WAITING: Wait indefinitely. object class, if not wake up, wait
TIME_WAITING: Waits for a limited period of time, and the thread waits for a specified time.
TERMINATED: State of terminating thread, thread has finished executing
The concepts of blocking and waiting are similar. Blocking requires waiting for external reasons. Waiting is generally an active call to a method, initiating an active wait, and waiting can also pass in parameters to determine the wait time.
public class Ch06 { public static void sleep(int i) { try { // Thread hibernates for 1 second Thread.sleep(i); System.out.println("Ha ha ha..."); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { sleep(3000); } }
The join method is meant to block the main thread
public class Ch07 { public static void main(String[] args) { Thread t1 = new Thread(() -> { for (int i = 0; i < 10; i++) { try { Thread.sleep(100); System.out.println("This is Thread 1---->" + i); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread t2 = new Thread(() -> { for (int i = 0; i < 10; i++) { try { Thread.sleep(100); System.out.println("This is Thread 2---->" + i); } catch (InterruptedException e) { e.printStackTrace(); } } }); t1.start(); t2.start(); try { // t1 queue jump t1.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("----------------------"); // Where the splitter line appears, the join method intentionally blocks the main thread } }
Thread Security
1. CPU Multi-core Cache Control
Physical memory: Hard disk memory (solid state hard disk, try not to choose a hybrid hard disk)
To improve program performance, CPU caches are processed the fastest, followed by memory, and the fastest on hard disks.
When the CPU processes memory data, if memory runs too slowly, it can slow down the CPU speed
To solve this problem, the CPU has designed a multilevel caching strategy
The CPU is divided into three levels of cache, each with L1 and L2 caches, but l3 caches are multi-core public
When CPU looks for data, CPU->l1->l2->l3->Memory->Hard Disk
From CPU to memory, 60-80 nanoseconds
From CPU to l3, 15 nanoseconds
From CPU to l2,3ns
From CPU to l1,1ns
Register: 0.3 nanoseconds
Further optimization, the CPU reads one data at a time, reading 64 bytes of data adjacent to it. (Cache rows)
MESI protocol
1. Modified state, this cache has been passive and its contents are different from those in main memory, so it is private to this cache
2. proprietary state, this cache is consistent with main memory, but not in other CPU s
3. Shared state, this cache is consistent with main memory, other caches also have
4. Invalid state, this cache is invalid and needs to be re-read from main memory
Instruction Rearrangement
Four instructions, four people write on four sheets of paper [Congratulations on making a fortune]
java memory model-JMM: try to achieve consistent access between hardware and operating system
public class Ch01 { { int a = 1; // 1 int b = 2; // 2 int c = a + b; // 3 /* Directive 3 cannot be placed before 1 and 2 However, there is no dependency between 1 and 2, and the editor can rearrange 1 and 2. It does not interfere with the order in which programs are executed. */ } public static void main(String[] args) { int [] nums = new int[]{1,2,3,4,5}; for (int i = 0; i < nums.length; i++) { System.out.println(nums[i]); } } }
To use the volatile keyword to ensure that a variable avoids rearrangement of instructions during a read-write operation, we add a command before the read-write operation. When the CPU encounters this command, it must wait until the previous execution is complete before continuing below.
package com.jsoft.afternoon; public class Ch02 { private static int x = 0,y = 0; private static int a = 0,b = 0; private static int count = 0; private volatile static int NUM = 1; public static void main(String[] args) throws InterruptedException { long start = System.currentTimeMillis(); for (;;) { Thread t1 = new Thread(() -> { a = 1; x = b; }); Thread t2 = new Thread(() -> { b = 1; y = a; }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("Executed altogether:" + count++ + "second"); if(x == 0 && y ==0){ long end = System.currentTimeMillis(); System.out.println("Time consuming:" +(end - start) + "In milliseconds, (" + x + "," + y + ")"); break; } a = 0;b = 0;x = 0;y = 0; } } /* We found that most of the test results felt correct, (0,1) or (1,0), with thread 1 executing first and thread 2 executing first. Logically, there will never be (0,0), if (0,0) means there is an instruction rearrangement and the execution is out of order. Use the volatile keyword to ensure that a variable avoids rearrangement of instructions during a read-write operation. We add an instruction before a read-write operation, and when the CPU encounters this instruction, it must wait until the previous execution is complete before it can continue with the next one. [Memory Barrier). */ }
Visibility between threads
Thread thread has been reading isOver in the cache, unable to perceive that the main thread has already put isOVer
volatile can force variable reads and writes to be changed directly in memory (so that the main thread's modifications are perceived in the thread)
public class Ch03 { private volatile static boolean isOver = false; private static int number = 0; public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new Runnable() { @Override public void run() { while(!isOver){ } System.out.println(number); } }); thread.start(); Thread.sleep(1000); number = 50; // Already changed, should be able to exit the loop isOver = true; } }
Thread contention
The best way to solve the problem is to lock
Synchronized lock, thread synchronization, when a method is decorated with synchronized, this method is called synchronization method. The keyword synchronized is an exclusive pessimistic lock implied by the JVM to allow only one thread to operate resources at a time.
tLock is Lock's default implementation based on AQS (Abstract Queued Synchronizer), which defaults to unfair locks
public class Ch04 { private volatile static int count = 0; public synchronized static void add() { count ++; } public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { for (int i = 0; i < 10000; i++) { add(); } }); Thread t2 = new Thread(() -> { for (int i = 0; i < 10000; i++) { add(); } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("The end result is:" + count); } }
Implementing thread security
(1) Data is immutable, all immutable objects must be thread-safe, and the caller of the method to implement the object does not need any more thread-safe safeguards.
Basic data types, such as final keyword modifiers, string
As long as an immutable object is created correctly, the external visible state will never change
(2) Mutually exclusive synchronization. Lock [Pessimistic Lock]
(3) Non-blocking synchronization. [Lockless programming], spin. We will use cas for this non-blocking synchronization
(4) No synchronization scheme. Multiple threads need to share data, but the data can be computed in separate threads to produce results. We can limit the visibility of shared data to a single thread, so that we don't need to synchronize to get the shared data back. I use mine, you use yours, to keep threads safe. ThreadLocal
public class Ch05 { private final static ThreadLocal<Integer> number = new ThreadLocal<>(); private static int count = 0; public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { // t1 use a count internally number.set(count); for (int i = 0; i < 10; i++) { number.set(count ++); System.out.println("t1-----" + number.get()); } } }); Thread t2= new Thread(new Runnable() { @Override public void run() { // t2 use a count internally number.set(count); for (int i = 0; i < 10; i++) { number.set(count ++); System.out.println("t2-----" + number.get()); } } }); t1.start(); t2.start(); } }