Article catalog
1. Thread introduction
The stack space is the fastest to operate, but the stack is very small. Usually, a large number of objects are placed in the heap space. The size of the stack and heap can be adjusted through the startup parameters of the JVM. When the stack space is used up, StackOverflowError will be caused, while insufficient heap and constant pool space will cause OutOfMemoryError.
String str = new String("hello");
In the above statement, the variable str is placed on the stack, the string object created with new is placed on the heap, and the literal "hello" is placed in the method area.
example:
Drive + call
Eat + play mobile phone
These actions can be abstracted as tasks. Although it seems that one mind has two functions, people have only one brain and can only process one task at a time.
The CPU is the same. Facing multiple tasks, it can only process one task at a time.
The difference between the main thread calling the run method and calling the start method to start the child thread is shown in the following figure.
- Threads are independent execution paths;
- When the program is running, even if it does not create its own thread, there will be multiple threads in the background, such as main thread and GC thread;
- Main () is called the main thread, which is the entry of the system and is used to execute the whole program;
- In a process, if multiple threads are opened up, the operation of threads is scheduled by the scheduler. The scheduler is closely related to the operating system, and the sequence can not be interfered by human beings.
- When operating on the same resource, there will be a problem of resource grabbing, and concurrency control needs to be added;
- Threads will bring additional overhead, such as CPU scheduling time and concurrency control overhead.
- Each thread interacts in its own working memory. Improper memory control will cause data inconsistency
2. Thread implementation
There are three implementations of threads:
2.1 inherit Thread class and override run method
Inherit the Thread class and override the run method. Create the object of this class and call start()
package com.sjmp.Thread01; /** * @ClassName ThreadTest * @Description TODO * @Author sjmp1573 * @Date DATE{TIME} */ public class ThreadTest { // Inherit the Thread class and override the run method public static class MyThread extends Thread{ @Override public void run() { System.out.println("I am a child thread"); } } public static void main(String[] args) { //Create a thread MyThread thread = new MyThread(); //Start thread thread.start(); } }
To download the file, you need to the commons io package in pom.xml.
Use this method to download network pictures.
package com.sjmp.demo01; import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.net.URL; /** * @author: sjmp1573 * @date: 2020/11/15 20:58 * @description: */ public class TestThread2 extends Thread{ // Network picture address private String url; // Saved file name private String name; public TestThread2(String url,String name){ this.url = url; this.name = name; } @Override public void run() { // After entering the thread, a downloader will be created. The downloader will download the corresponding resources by passing in url and name through the downloader method WebDownloader webDownloader = new WebDownloader(); webDownloader.downloader(url,name); System.out.println("Downloaded a file named:"+ name); } public static void main(String[] args) { // This is the main method of the TestThread2 class // Create three subclasses that inherit Thread TestThread2 test01 = new TestThread2("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=3796445054,4193265240&fm=26&gp=0.jpg", "test01"); TestThread2 test02 = new TestThread2("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1605454961548&di=c3b49cc5869f058a6cded1434ea56f85&imgtype=0&src=http%3A%2F%2Fpic1.win4000.com%2Fwallpaper%2F5%2F538ec3134b63b.jpg", "test02"); TestThread2 test03 = new TestThread2("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2044644877,1766802492&fm=15&gp=0.jpg", "test03"); // And start the thread test01.start(); test02.start(); test03.start(); } } //Downloader, this is a class class WebDownloader{ // Download method public void downloader(String url,String name){ try { FileUtils.copyURLToFile(new URL(url),new File(name)); } catch (IOException e) { e.printStackTrace(); System.out.println("IO Abnormal, downloader There is a problem with the method"); } } }
<!-- https://mvnrepository.com/artifact/commons-io/commons-io --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.8.0</version> </dependency>
2.2 inherit the Runnable interface and create a tree object
Inherit the Runnable interface, create a tree object, pass in the implementation class, and start the start method
package com.sjmp.Thread01; /** * @ClassName ThreadRunnableTest * @Description TODO * @Author sjmp1573 * @Date DATE{TIME} */ public class ThreadRunnableTest { public static class MyThread implements Runnable { @Override public void run() { System.out.println("I am a child thread --Runnable"); } } public static void main(String[] args) { MyThread thread = new MyThread(); new Thread(thread).start(); new Thread(thread).start(); } }
Comparison of the above two methods:
Inherit Thread class
- The subclass inherits the Thread class and has multithreading capability
- Start thread: subclass object. start()
- Not recommended: avoid the limitation of OOP single inheritance
Implement Runnable interface
- The implementation interface Runnable has multithreading capability
- Start Thread: pass in the target object + Thread object. start()
- Recommended: avoid the limitation of single inheritance and facilitate the use of the same object by multiple threads.
Examples of train ticket grabbing:
Runnable implementation Multithreading , create a real ticketRunnable that can be shared with multiple threads.
package com.sjmp.demo01; /** * @author: sjmp1573 * @date: 2020/11/15 21:45 * @description: */ // Multiple threads operate on the same object at the same time // Example of buying a train ticket // Problems found: multiple threads operate on the same resource, unsafe threads and disordered data! public class TicketRunnable implements Runnable{ private int ticketNums = 10; @Override public void run() { while (true){ if (ticketNums<=0){ break; } // Analog delay /* IllegalArgumentException if the value of {@code millis} is negative, or the value of {@code nanos} is not in the range {@code 0-999999} InterruptedException if any thread has interrupted the current thread. The <i>interrupted status</i> of the current thread is cleared when this exception is thrown. */ try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } ticketNums--; System.out.println(Thread.currentThread().getName()+"-->Got the second"+ticketNums+"ticket"); } } public static void main(String[] args) { // Class that implements the Runnable interface and creates its instance TicketRunnable ticketRunnable = new TicketRunnable(); // Tickelrunnable instances can be used for multiple threads where resources are shared. new Thread(ticketRunnable,"01 Xiao Ming+++++").start(); new Thread(ticketRunnable,"02 teacher-----").start(); new Thread(ticketRunnable,"03 cattle=====").start(); } }
"D:\Program Files (x86)\Java\bin\java.exe" "-javaagent:D:\Program Files (x86)\IDEA\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar=64250:D:\Program Files (x86)\IDEA\IntelliJ IDEA 2019.3.3\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files (x86)\Java\jre\lib\charsets.jar;D:\Program Files (x86)\Java\jre\lib\deploy.jar;D:\Program Files (x86)\Java\jre\lib\ext\access-bridge-64.jar;D:\Program Files (x86)\Java\jre\lib\ext\cldrdata.jar;D:\Program Files (x86)\Java\jre\lib\ext\dnsns.jar;D:\Program Files (x86)\Java\jre\lib\ext\jaccess.jar;D:\Program Files (x86)\Java\jre\lib\ext\jfxrt.jar;D:\Program Files (x86)\Java\jre\lib\ext\localedata.jar;D:\Program Files (x86)\Java\jre\lib\ext\nashorn.jar;D:\Program Files (x86)\Java\jre\lib\ext\sunec.jar;D:\Program Files (x86)\Java\jre\lib\ext\sunjce_provider.jar;D:\Program Files (x86)\Java\jre\lib\ext\sunmscapi.jar;D:\Program Files (x86)\Java\jre\lib\ext\sunpkcs11.jar;D:\Program Files (x86)\Java\jre\lib\ext\zipfs.jar;D:\Program Files (x86)\Java\jre\lib\javaws.jar;D:\Program Files (x86)\Java\jre\lib\jce.jar;D:\Program Files (x86)\Java\jre\lib\jfr.jar;D:\Program Files (x86)\Java\jre\lib\jfxswt.jar;D:\Program Files (x86)\Java\jre\lib\jsse.jar;D:\Program Files (x86)\Java\jre\lib\management-agent.jar;D:\Program Files (x86)\Java\jre\lib\plugin.jar;D:\Program Files (x86)\Java\jre\lib\resources.jar;D:\Program Files (x86)\Java\jre\lib\rt.jar;E:\SJMP\SpringProject\out\production\LeetCode" com.sjmp.TicketRunnable 03 cattle=====-->Got the 8th ticket 01 Xiao Ming+++++-->Got the 8th ticket 02 teacher------->Got the 8th ticket 03 cattle=====-->Got the seventh ticket 02 teacher------->Got the seventh ticket 01 Xiao Ming+++++-->Got the seventh ticket 02 teacher------->Got the sixth ticket 03 cattle=====-->Got the sixth ticket 01 Xiao Ming+++++-->Got the sixth ticket 01 Xiao Ming+++++-->Got the fifth ticket 03 cattle=====-->Got the fourth ticket 02 teacher------->Got the fifth ticket 03 cattle=====-->Got the third ticket 02 teacher------->Got the third ticket 01 Xiao Ming+++++-->Got the third ticket 02 teacher------->Got the first ticket 03 cattle=====-->Got the second ticket 01 Xiao Ming+++++-->Got the second ticket 02 teacher------->Got ticket 0 01 Xiao Ming+++++-->Got the second-1 ticket 03 cattle=====-->Got the second-1 ticket Process finished with exit code 0
2.3 implement Callable interface (understand)
- To implement the Callable interface, the return value type is required
- When overriding the call method, you need to throw an exception
- Create target object
- Create execution service: ExecutorService = Executor.newFixedThreadPool(1);
- Submit execution: Future result1 = ser.submit(1);
- Get result: boolean r1 = result.get()
- Shutdown service: ser.shutdownNow():
package com.sjmp.demo01; import java.util.concurrent.*; /** * @author: sjmp1573 * @date: 2020/11/15 22:16 * @description: */ public class ThreadByCallable implements Callable<Boolean> { // Network picture address private String url; // Saved file name private String name; public ThreadByCallable(String url,String name){ this.url = url; this.name = name; } @Override public Boolean call() throws Exception { // After entering the thread, a downloader will be created. The downloader will download the corresponding resources by passing in url and name through the downloader method WebDownloader webDownloader = new WebDownloader(); webDownloader.downloader(url,name); System.out.println("Downloaded a file named:"+ name); return true; } public static void main(String[] args) throws ExecutionException, InterruptedException { // This is the main method of the TestThread2 class // Create three subclasses that inherit Thread ThreadByCallable test01 = new ThreadByCallable("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=3796445054,4193265240&fm=26&gp=0.jpg", "test01"); ThreadByCallable test02 = new ThreadByCallable("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1605454961548&di=c3b49cc5869f058a6cded1434ea56f85&imgtype=0&src=http%3A%2F%2Fpic1.win4000.com%2Fwallpaper%2F5%2F538ec3134b63b.jpg", "test02"); ThreadByCallable test03 = new ThreadByCallable("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2044644877,1766802492&fm=15&gp=0.jpg", "test03"); // Create execution service: ExecutorService service = Executors.newFixedThreadPool(3); // Submit for execution Future<Boolean> submit01 = (Future<Boolean>) service.submit(test01); Future<Boolean> submit02 = (Future<Boolean>) service.submit(test02); Future<Boolean> submit03 = (Future<Boolean>) service.submit(test03); boolean rs1 = submit01.get(); boolean rs2 = submit02.get(); boolean rs3 = submit03.get(); // Shut down service service.shutdownNow(); } } //Downloader, this is a class class WebDownloader{ // Download method public void downloader(String url,String name){ try { FileUtils.copyURLToFile(new URL(url),new File(name)); } catch (IOException e) { e.printStackTrace(); System.out.println("IO Abnormal, downloader There is a problem with the method"); } } }
---------------------------------------Gorgeous dividing line----------------------------------------
The following code comes from the beauty of Java Concurrent Programming
The CallableTest class implements the call() method of the Callable interface. In the main() function, first create a FutureTask object (the constructor is CallableTest instance), then use the created FutureTask object as a task to create a thread and start it, and finally wait for the result to be returned after the task is executed through futureTask.get().
public class FutureTask<V> implements RunnableFuture<V>{} public interface RunnableFuture<V> extends Runnable, Future<V>
package com.sjmp.practice; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; /** * @author: sjmp1573 * @date: 2021/4/20 21:12 * @description: */ public class CallableTest implements Callable<String> { @Override public String call() throws Exception { return "Hello CallableThread"; } public static void main(String[] args) { CallableTest callableTest = new CallableTest(); FutureTask<String> futureTask = new FutureTask<>(callableTest); new Thread(futureTask).start(); try { String s = futureTask.get(); System.out.println(s); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
Hello CallableThread Process finished with exit code 0
2.4 Lamda expression
Lamda expressions belong to the concept of functional programming
(paraems) -> expressionp[expression] (params) -> statement[sentence] (params) -> {statements} a->System.out.println("i like lamda-->"+a); new Thread(()->System.out.println("Multithreaded learning...")).start();
-
Understanding the Functional Interface is the key to learning java 8 lambda expressions.
-
Definition of functional interface:
- Any interface that contains only one abstract method is a functional interface.
- For functional interfaces, you can create objects of the interface through Lamda expressions.
Evolution of Lamda expression:
package com.sjmp.demo02; /** * @author: sjmp1573 * @date: 2020/11/16 20:11 * @description: */ public class LamdaExpression { // 3.2 the second method to implement functional interfaces is static inner classes static class Like2 implements ILike{ @Override public void lamda() { System.out.println("------3.2 Static inner classes implement functional interfaces-----"); } } public static void main(String[] args) { // 3.1 the first method to realize functional interface ILike like1 = new Like1(); like1.lamda(); System.out.println("--3.1 Common methods to implement functional interfaces--"); // 3.2 the second method to implement functional interfaces is static inner classes new Like2().lamda(); // 3.3 local internal classes implement functional interfaces class Like3 implements ILike{ @Override public void lamda() { System.out.println("------3.3 Local inner classes implement functional interfaces--------"); } } new Like3().lamda(); // 3.4 anonymous inner classes implement functional interfaces new ILike() { @Override public void lamda() { System.out.println("------3.4 Anonymous inner classes implement functional interfaces----------"); } }.lamda(); // 3.5 lamda expression to realize functional interface ILike like5 = ()->{ System.out.println("--3.5 lamda Expressions implement functional interfaces--"); }; like5.lamda(); } } // 1. Define a functional interface interface ILike{ void lamda(); } // 2. Implementation class class Like1 implements ILike{ @Override public void lamda() { } }
2.5 static proxy mode
Multithreaded Thread is the proxy and Runnable is the proxied object:
package com.sjmp.demo02; /** * @author: sjmp1573 * @date: 2020/11/16 19:32 * @description: */ //This is an agent public class StaticProxy implements Marry{ private Marry you; public StaticProxy(You you){ this.you = you; } public static void main(String[] args) { // Runnable is the proxied object and Thread is the proxy /* new Thread(new Runnable() { @Override public void run() { System.out.println("----Marriage - "); } }).start(); */ // Using lamda expressions new Thread(()->{ System.out.println("----marry----"); }).start(); new StaticProxy(new You()).HappyMarry(); } @Override public void HappyMarry() { doBefore(); you.HappyMarry(); doAfter(); } public static void doBefore(){ System.out.println("-----Premarital arrangement------"); } public static void doAfter(){ System.out.println("-------Receive money after marriage-----"); } } interface Marry{ void HappyMarry(); } //Real character, Mary class You implements Marry{ @Override public void HappyMarry() { System.out.println("----- The principal began to marry------"); } }
3. Five states of threads
- establish
- be ready
- block
- function
- death
3.1 some common methods of thread
Some methods of threading are shown in the following figure:
3.1.1 thread sleep -- sleep()
- sleep (time) specifies the number of milliseconds that the current thread is blocked;
- Exception InterruptedException exists in sleep;
- After the sleep time reaches, the thread enters the ready state;
- sleep can simulate network delay, countdown, etc;
- Sleep each object has a lock, and sleep will not release the lock;
The usefulness of the sleep() method
package com.sjmp.method; import java.awt.*; import java.text.SimpleDateFormat; import java.util.Date; import static java.lang.Thread.*; /** * @author: sjmp1573 * @date: 2020/11/16 21:50 * @description: */ public class TestSleep { public static void main(String[] args) throws InterruptedException { // Thread.sleep() is used to count down // tenStop(); // Print current system time Date date = new Date(System.currentTimeMillis()); boolean flag = true; int i = 5; while(flag){ if(--i<=0){ flag = false; } try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date)); date = new Date(System.currentTimeMillis());//Update time } } // Write a countdown public static void tenStop() throws InterruptedException { int num = 10; while(true){ try{ sleep(1000); }catch ( InterruptedException e){ e.printStackTrace(); } if (num<=0){ break; } System.out.println(num--); } } }
3.1.2 thread comity - yield()
- Comity thread, which allows the currently executing thread to pause without blocking;
- Change the thread from running state to ready state;
- Let the CPU schedule again. It is possible to schedule the comity thread.
package com.sjmp.method; /** * @author: sjmp1573 * @date: 2020/11/16 22:22 * @description: */ public class TestYield implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"Thread turned on"); Thread.yield(); System.out.println(Thread.currentThread().getName()+"Thread ended"); } public static void main(String[] args) { TestYield testYield = new TestYield(); Thread threadA = new Thread(testYield,"threadA"); Thread threadB = new Thread(testYield,"threadB"); threadA.start(); threadB.start(); } }
3.1.2 merge threads - Join()
Join merge threads. After this thread completes execution, execute other threads. Other threads are blocked.
Imagine jumping in line.
package com.sjmp.method; /** * @author: sjmp1573 * @date: 2020/11/17 20:46 * @description: */ public class TestJoin implements Runnable{ @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println("Thread : "+i); } } public static void main(String[] args) throws InterruptedException { TestJoin join = new TestJoin(); Thread thread = new Thread(join); thread.start(); for (int i = 0; i < 200; i++) { if (i==100){ thread.join(); } System.out.println("main :"+i); } } }
3.2 ways to stop threads
- The stop() and destroy() methods provided by JDK are not recommended. [deprecated]
- It is recommended that the thread stop itself
- It is recommended to use a flag bit to terminate the variable. When flag == false, the thread will be terminated.
package com.sjmp.demo02; /** * @author: sjmp1573 * @date: 2020/11/16 21:33 * @description: */ public class ThreadStop implements Runnable{ private boolean flag = true; @Override public void run() { int i = 0; while (flag){ System.out.println("--- ThreadStop ---"+i); i++; } } public void stop(){ this.flag = false; } public static void main(String[] args) { ThreadStop threadStop = new ThreadStop(); Thread thread = new Thread(threadStop); thread.start(); for (int i = 0; i < 1000; i++) { if (i==900){ threadStop.stop(); } System.out.println("--- main ---"+i); } } }
3.3 thread state observation
Thread.State
Thread state. A thread can be in one of the following states:
- NEW
Threads that have not been started are in this state. - RUNNABLE
The thread executing in the Java virtual machine is in this state. - BLOCKED
Threads that are blocked waiting for a monitor lock are in this state. - WAITING
A thread that is waiting for another thread to perform a specific action is in this state. - TERMINATED
The exited thread is in this state.
A thread can be in a state at a given point in time.
package com.sjmp.method; /** * @author: sjmp1573 * @date: 2020/11/17 21:10 * @description: */ public class TestState{ public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(()->{ for (int i = 0; i < 20; i++) { try { System.out.println(i); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("Thread Execution is over!"); }); Thread.State state = thread.getState(); System.out.println(state); thread.start(); System.out.println(thread.getState()); System.out.println(" I'm starting to cycle "); while(state != Thread.State.TIMED_WAITING){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } state = thread.getState(); System.out.println(state); } } }
3.4 thread priority
- Java provides a thread scheduler to monitor all threads that enter the ready state after startup. The thread scheduler determines which thread should be scheduled to execute according to priority.
- The priority of threads is expressed in numbers, ranging from 1 to 10
Thread.MIN_PRIORITY=1;
Thread.MAX_PRIORITY=10;
Thread.NORM_PRIORITY=5; - Change or get priority in the following ways
getPriority().setPriority(int xxx)
Priority setting is recommended in start() Before dispatching
package com.sjmp.method; /** * @author: sjmp1573 * @date: 2020/11/17 21:32 * @description: */ public class TestPriority implements Runnable { @Override public void run() { System.out.println("Current thread:"+Thread.currentThread().getName()); } public static void main(String[] args) { TestPriority runnable = new TestPriority(); Thread thread01 = new Thread(runnable,"01"); Thread thread02 = new Thread(runnable,"02"); Thread thread03 = new Thread(runnable,"03"); Thread thread04 = new Thread(runnable,"04"); Thread thread05 = new Thread(runnable,"05"); thread01.setPriority(Thread.MAX_PRIORITY); thread02.setPriority(7); thread03.setPriority(6); thread04.setPriority(5); thread05.setPriority(4); thread01.start(); thread02.start(); thread03.start(); thread04.start(); thread05.start(); } }
3.5 daemon thread
- Threads are divided into user threads and daemon threads
- The virtual machine must ensure that the user thread has completed execution
- The virtual machine does not have to wait for the daemon thread to finish executing
- For example, record operation logs in the background, monitor memory garbage collection and wait
package com.sjmp.method; /** * @author: sjmp1573 * @date: 2020/11/17 21:43 * @description: */ public class TestsetDaemon { public static void main(String[] args) { God god = new God(); You you = new You(); Thread thread = new Thread(god); thread.setDaemon(true); thread.start(); new Thread(you).start(); } } class God implements Runnable{ @Override public void run() { while (true){ System.out.println("Daemoning..."); } } } class You implements Runnable{ @Override public void run() { for (int i = 0; i < 365; i++) { System.out.println("living..."); } System.out.println("game over---------------------"); } }
3.6 concurrency
The same object is operated by multiple threads at the same time
In real life, we will encounter the problem of "many people want to use the same resource". For example, everyone wants to eat in the canteen. The most natural solution is to queue up one by one.
When dealing with multithreading problems, multiple threads access the same object, and some threads want to modify the object. At this time, we need thread synchronization. Thread synchronization is actually a waiting mechanism, and multiple threads need to access at the same time! The threads of this object enter the waiting pool of this object to form a queue, wait for the previous thread to use it, and then use it by the next thread.
Conditions for thread safety:
Queues and locks
4. Thread synchronization
Because multiple threads of the same process share the same storage space, it brings convenience and access conflict. In order to ensure the correctness of data access in the method, the lock mechanism synchronized is added during access. When one thread obtains the exclusive lock of the object and monopolizes resources, other threads must wait and release the lock after use. There are the following problems :
- A thread holding a lock will cause all other threads that need the lock to hang;
- In multi thread competition, locking and releasing locks will lead to more context switching and scheduling delays, resulting in performance problems;
- If a high priority thread waits for a low priority thread to release the lock, it will lead to priority inversion and performance problems
4.1 thread unsafe example
Example: unsafe ticket selling
package com.sjmp.Concurrent; /** * @author: sjmp1573 * @date: 2020/11/17 22:03 * @description: */ public class UnsafeBuyTicket { } class BuyTicket implements Runnable{ private int ticketNum = 10; boolean flag = true; @Override public void run() { while (flag){ buy(); } System.out.println("sell out"); } //Add the keyword synchronized to become thread safe public void buy(){ if (ticketNum<=0){ flag = false; return; } try { // Simulated ticket buying delay Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" get ticket:"+ticketNum); ticketNum--; } public static void main(String[] args) { BuyTicket buyTicket = new BuyTicket(); Thread thread01 = new Thread(buyTicket,"01"); Thread thread02 = new Thread(buyTicket,"02"); Thread thread03 = new Thread(buyTicket,"03"); thread01.start(); thread02.start(); thread03.start(); } }
Example: bank withdrawal
Code omission
Example: thread unsafe collection
Refer to: Why ArrayList is thread unsafe: https://blog.csdn.net/qq_42183409/article/details/100586255
package com.sjmp.Concurrent; import java.util.ArrayList; /** * @author: sjmp1573 * @date: 2020/11/17 22:22 * @description: */ public class UnsafeList { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); for (int i = 0; i < 10000; i++) { new Thread(()->{ list.add(Thread.currentThread().getName()); }).start(); } try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size()); } }
Example: thread safe collection: CopyOnWriteArrayList
package com.sjmp.Concurrent; import java.util.concurrent.CopyOnWriteArrayList; /** * @author: sjmp1573 * @date: 2020/11/18 9:48 * @description: */ public class TestJUC { public static void main(String[] args) { CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>(); for (int i = 0; i < 1000; i++) { new Thread(()->{ list.add(Thread.currentThread().getName()); }).start(); } try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size()); } }
4.2 synchronization method
- Because we can use the keyword private keyword to ensure that data objects can only be accessed by methods, we just need to propose a mechanism for methods. This mechanism is the synchronized keyword, which includes two methods:
Synchronized method and synchronized block
Synchronization method:
public synchronized void method(int args){}
- Synchronized methods control the access of "objects". Each object corresponds to a lock. Each synchronized method must obtain the lock of the object calling the method before it can be executed. Otherwise, the thread will block. Once the method is executed, it will monopolize the lock until the method returns, and the blocked thread can obtain the lock and continue to execute.
Defect: declaring a large method synchronized will affect efficiency.
4.3 synchronization block
Synchronization block: synchronized(Obj) {}
Obj calls it a synchronization monitor
- Obj can be any object, but it is recommended to use shared resources as synchronization monitors
- There is no need to specify a synchronization monitor in the synchronization method, because the synchronization monitor of the synchronization method is this, the object itself, or class
- Synchronization monitor execution
- The first thread accesses, locks the synchronization monitor, and executes the code in it
- The second thread accesses and finds that the synchronization monitor is locked and cannot be accessed
- After the first thread is accessed, unlock the synchronization monitor
- The second thread accesses, finds that the synchronization monitor has no lock, and then locks and accesses
4.3 deadlock
Multiple threads occupy some shared resources and wait for the resources occupied by other threads to run. As a result, two or more threads are waiting for each other to release resources and stop execution. When a synchronization block holds "locks of more than two objects" at the same time, the problem of "deadlock" may occur.
Four necessary conditions for deadlock generation:
- Mutex condition: a resource can only be used by one process at a time.
- Request and hold condition: when a process is blocked by requesting resources, it will hold on to the obtained resources.
- Conditions of non deprivation: the resources obtained by the process cannot be forcibly deprived before they are used up.
- Circular waiting condition: a circular waiting resource relationship is formed between several processes.
The deadlock can be avoided by breaking any of the above four conditions.
4.4 Lock
- Since JDK 5.0, Java has provided a more powerful thread synchronization mechanism -- synchronization is achieved by displaying and defining synchronization Lock objects. Synchronous locks use Lock objects as
- The java.util.concurrent.locks.Lock interface is a tool that controls multiple threads to access shared resources. Lock provides exclusive access to shared resources. Only one thread can lock the lock object at a time. Threads should obtain the lock object before accessing shared resources
- ReentrantLock class implements Lock. It has the same concurrency and memory semantics as synchronized. ReentrantLock is commonly used in thread safety control, which can display locking and releasing locks.
synchronized vs Lock
- Lock is a display lock (opened and closed manually), synchronized is an implicit lock, and it is automatically released out of the scope
- Lock only has code lock, and synchronized has code block lock and method lock
- Using Lock lock, the JVM will spend less time scheduling threads and perform better. And has better extensibility (providing more subclasses)
- Lock > synchronize code block (it has entered the method body and allocated corresponding resources) > synchronize method (outside the method body)
package com.sjmp.Concurrent; import java.util.concurrent.locks.ReentrantLock; /** * @author: sjmp1573 * @date: 2020/11/18 16:52 * @description: */ public class TestLock { public static void main(String[] args) { Ticket ticket = new Ticket(); new Thread(ticket).start(); new Thread(ticket).start(); new Thread(ticket).start(); } } class Ticket extends Thread{ private int ticketNums = 10; // Define lock lock private final ReentrantLock lock = new ReentrantLock(); @Override public void run() { while(true){ try{ lock.lock(); if (ticketNums>0){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(ticketNums--); }else{ break; } }finally { lock.unlock(); } } } }
5. Thread communication
Application scenario: producer and consumer issues
-
Suppose that only one product can be stored in the warehouse, the producer puts the produced products into the warehouse, and the consumer takes the products from the warehouse for consumption.
-
If there is no product in the warehouse, the producer will put the product into the warehouse, otherwise stop production and wait until the product in the warehouse is taken away by the consumer.
-
If there are products in the warehouse, consumers can take away the products for consumption, otherwise they will stop consumption until the products are put in the warehouse again.
This is a thread synchronization problem. Producers and consumers share the same resource, and producers and consumers are interdependent and conditional on each other.
- For producers, they should inform consumers to wait before producing products, and after producing products, they need to inform consumers to consume immediately
- For consumers, after consumption, they should inform producers that they have finished consumption and need to produce new products for consumption
- In the producer consumer problem, synchronized is not enough
synchronized prevents concurrent updates to the same shared resource, enabling synchronization
synchronized cannot be used for message passing (Communication) between different threads
5.1 several methods to solve the communication problem between threads
Note: all methods of Object class can only be used in synchronization methods or synchronization code blocks, otherwise the exception legalmonitorstateexception will be thrown
5.2 way to solve the communication between threads 1: pipe method
Concurrent writing model "producer / consumer model" – > management process method
- Producer: module responsible for production data (may be method, object, thread, process);
- Consumer: module responsible for processing data (may be method, object, thread, process)
- Buffer: consumers cannot directly use the producer's data. There is a buffer between them
The producer puts the produced data into the buffer, and the consumer takes out the data from the buffer
package com.sjmp.advanced; /** * @author: sjmp1573 * @date: 2020/11/18 20:52 * @description: */ // Producer, consumer, product, buffer public class TestPC { public static void main(String[] args) { SynContainer container = new SynContainer(); new Productor(container).start(); new Consumer(container).start(); } } // producer class Productor extends Thread{ SynContainer container; public Productor(SynContainer container){ this.container = container; } @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("Produced"+i+"Chicken"); container.push(new Chicken(i)); } } } class SynContainer{ // The size of a container is required Chicken[] chickens = new Chicken[10]; // Container counter int count = 0; // The producer puts in the product public synchronized void push(Chicken chicken){ // If the container is full, it needs to wait for consumers to consume if (count == chickens.length){ // Inform consumers of consumption, production and waiting try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } chickens[count] = chicken; count++; this.notifyAll(); } // Consumer products public synchronized Chicken pop(){ // Judge whether it can be consumed if(count==0){ // Wait for producers to produce, consumers to wait try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // If you can consume count--; Chicken chicken = chickens[count]; // You can notify the consumer this.notifyAll(); return chicken; } } class Consumer extends Thread{ SynContainer container; public Consumer(SynContainer container){ this.container = container; } @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("Consumption-->"+container.pop().id+"Chicken"); } } } // product class Chicken{ int id; //Product number public Chicken(int id){ this.id = id; } }
5.3 solution to communication between threads 1: semaphore method
package com.sjmp.advanced; /** * @author: sjmp1573 * @date: 2020/11/18 21:34 * @description: */ public class TestPC2 { public static void main(String[] args) { TV tv = new TV(); new Player(tv).start(); new Wathcher(tv).start(); } } //Producer actor class Player extends Thread{ TV tv; public Player(TV tv){ this.tv = tv; } @Override public void run() { for (int i = 0; i < 20; i++) { if(i%2==0){ this.tv.play("Happy base camp"); }else{ this.tv.play("make progress every day"); } } } } //audience class Wathcher extends Thread{ TV tv; public Wathcher(TV tv){ this.tv = tv; } @Override public void run() { for (int i = 0; i < 20; i++) { tv.watch(); } } } //Products -- programs class TV{ // The actor performs and the audience waits for T // The audience watched and the actors waited for F String voice; // perform boolean flag = true; // perform public synchronized void play(String voice){ if(!flag){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("The actors performed: "+voice); // Inform the audience to watch this.notifyAll();// Notification wake up this.voice = voice; this.flag = !flag; } // watch public synchronized void watch(){ if (flag){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("Watched: "+voice); // Inform the actors to perform this.notifyAll(); this.flag = !this.flag; } }
5.4 using thread pool
Background: resources that are often created and destroyed and used heavily, such as threads in concurrency, have a great impact on performance.
Idea: create many threads in advance, put them into the thread pool, get them directly when using them, and put them back into the pool after use.
It can avoid frequent creation, destruction and reuse. Similar to public transport in life.
advantage:
Improved response time (reduced time to create new threads)
Reduce resource consumption (reuse threads in the thread pool and do not need to be created every time)
Easy thread management
- corePoolSize: the size of the core pool
- maximumPoolSize: maximum number of threads
- keepAliveTime: how long will the thread terminate when it has no task
package com.sjmp.advanced; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @author: sjmp1573 * @date: 2020/11/18 21:53 * @description: */ public class TestPool { public static void main(String[] args) { // 1. Create service and thread pool ExecutorService service = Executors.newFixedThreadPool(10); // The newFixedThreadPool parameter is the thread pool size // implement service.execute(new MyThread()); service.execute(new MyThread()); service.execute(new MyThread()); service.execute(new MyThread()); // 2. Close the connection service.shutdown(); } } class MyThread implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()); } }
6 supplementary contents
package com.sjmp.Thread01; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; /** * @ClassName ThreadFutureTest * @Description TODO * @Author sjmp1573 * @Date DATE{TIME} */ public class ThreadFutureTest { public static class CallerTask implements Callable<String>{ @Override public String call() throws Exception { return "Thread-Callable- hello"; } } public static void main(String[] args) { // Create asynchronous task FutureTask<String> futureTask = new FutureTask<>(new CallerTask()); new Thread(futureTask).start(); try { String result = futureTask.get(); System.out.println(result); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
The CallerTask class in the above code implements the call() method of the Callable interface. In the main function, first create a FutrueTask object (the constructor is an instance of CallerTask), then use the created FutrueTask object as a task to create a thread and start it, and finally wait for the task to complete execution and return results through futureTask.get().
Summary: the advantage of using inheritance is that it is convenient to pass parameters. You can add member variables in subclasses, set parameters through the set method or pass them through the constructor. If Runnable is used, only variables declared as final in the main Thread can be used. The bad thing is that Java does not support multi inheritance. If you inherit the Thread class, the subclass can no longer inherit other classes, while Runable does not have this restriction. Neither of the first two methods can get the return result of the task, but Futuretask can.