Comprehensive explanation of Java multithreading and multi case

Posted by poison on Tue, 18 Jan 2022 19:54:38 +0100

This chapter mainly clarifies multithreading

Processes and threads

  • Process is the unit of system resource allocation, and thread is the unit of CPU scheduling and execution

There are three ways to create threads

Inherit Thread class (not recommended to avoid the limitation of OOP single inheritance)

  • The custom Thread class inherits the Thread class
  • Rewrite the run() method to write the thread execution body
  • Create a thread object and call the start() method to start the thread
public class ThreadTest extends Thread{
    //Thread entry point
    @Override
    public void run(){
        //Thread body
        for (int i = 1; i <= 5; i++) {
            System.out.println("Already implemented"+i+"second");
        }
    }
    public static void main(String[] args) {
        //Create thread object
        ThreadTest threadTest=new ThreadTest();
        threadTest.start();
    }
}
//Output results:
Executed for 1 second
 It has been executed for 2 seconds
 It has been executed for 3 seconds
 It has been executed for 4 seconds
 It has been executed for 5 seconds

Case: download pictures

public class ThreadTest extends Thread{
    private String url;
    private String name;

    public ThreadTest(String url, String name) {
        this.url = url;
        this.name = name;
    }

    @Override
    public void run(){
        WebDownloader webDownloader=new WebDownloader();
        try {
            webDownloader.downloader(url,name);
            System.out.println("This file has been downloaded:"+name);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        ThreadTest t1=new ThreadTest("https://cdn.jsdelivr.net/gh/jasper807/picgo/cover/javaio.png","src/img/p1.png");
        ThreadTest t2=new ThreadTest("https://cdn.jsdelivr.net/gh/jasper807/picgo/cover/javaio.png","src/img/p2.png");
        ThreadTest t3=new ThreadTest("https://cdn.jsdelivr.net/gh/jasper807/picgo/cover/javaio.png","src/img/p3.png");
        //Start thread
        t1.start();
        t2.start();
        t3.start();
    }
}
//File download tool class
class WebDownloader{
    //Remote path, storage name
    public  void downloader(String url,String name) throws IOException {
        FileUtils.copyURLToFile(new URL(url),new File(name));
    }
}
//Output results:
This file has been downloaded: src/img/p3.png
 This file has been downloaded: src/img/p2.png
 This file has been downloaded: src/img/p1.png

Implement the Runnable interface (recommended to facilitate the use of the same object by multiple threads)

  • The custom thread class implements the Runnable interface
  • Implement the run() method and write the thread execution body
  • New thread (pass in the target object) start()
public class RunnableTest implements Runnable{
    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println("Already implemented"+i+"second");
        }
    }

    public static void main(String[] args) {
        new Thread(new RunnableTest()).start();
    }
}
//Output results:
Executed for 1 second
 It has been executed for 2 seconds
 It has been executed for 3 seconds
 It has been executed for 4 seconds
 It has been executed for 5 seconds

Case: download pictures

public class RunnableTest implements Runnable{
    private String url;
    private String name;

    public RunnableTest(String url, String name) {
        this.url = url;
        this.name = name;
    }

    @Override
    public void run() {
        WebDownloader webDownloader=new WebDownloader();
        try {
            webDownloader.downloader(url,name);
            System.out.println("This file has been downloaded:"+name);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new Thread(new ThreadTest("https://cdn.jsdelivr.net/gh/jasper807/picgo/cover/javaio.png","src/img/p1.png")).start();
        new Thread(new ThreadTest("https://cdn.jsdelivr.net/gh/jasper807/picgo/cover/javaio.png","src/img/p2.png")).start();
        new Thread(new ThreadTest("https://cdn.jsdelivr.net/gh/jasper807/picgo/cover/javaio.png","src/img/p3.png")).start();
    }
}
//Output results:
This file has been downloaded: src/img/p3.png
 This file has been downloaded: src/img/p1.png
 This file has been downloaded: src/img/p2.png

Implement Callable interface (just 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 executorservice executorservice = executors newFixedThreadPool(1);
  • Submit and execute future < Boolean > result1 = Ser submit(t1);
  • Get the result Boolean R1 = result1 get();
  • Close the service Ser shutdownNow();
public class CallableTest implements Callable<Boolean> {
    private String url;
    private String name;

    public CallableTest(String url, String name) {
        this.url = url;
        this.name = name;
    }

    @Override
    public Boolean call() throws Exception {
        WebDownloader webDownloader=new WebDownloader();
        try {
            webDownloader.downloader(url,name);
            System.out.println("This file has been downloaded:"+name);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return true;
    }

    public static void main(String[] args) {
        CallableTest t1=new CallableTest("https://cdn.jsdelivr.net/gh/jasper807/picgo/cover/javaio.png","src/img/p1.png");
        CallableTest t2=new CallableTest("https://cdn.jsdelivr.net/gh/jasper807/picgo/cover/javaio.png","src/img/p2.png");
        CallableTest t3=new CallableTest("https://cdn.jsdelivr.net/gh/jasper807/picgo/cover/javaio.png","src/img/p3.png");
        //Create execution service
        ExecutorService ex= Executors.newFixedThreadPool(3);
        //Submit for execution
        Future<Boolean> result1= (Future<Boolean>) ex.submit(t1);
        Future<Boolean> result2= (Future<Boolean>) ex.submit(t2);
        Future<Boolean> result3= (Future<Boolean>) ex.submit(t3);
        //Get results
        try{
            if (result1.get()!=null){
                boolean r1= result1.get();
                System.out.println("r1="+r1);
            }
            if (result2.get()!=null){
                boolean r2= result2.get();
                System.out.println("r2="+r2);
            }
            if (result3.get()!=null){
                boolean r3= result3.get();
                System.out.println("r3="+r3);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        //Shut down service
        ex.shutdownNow();
    }
}
//Output results:
This file has been downloaded: src/img/p1.png
 This file has been downloaded: src/img/p3.png
r1=true
 This file has been downloaded: src/img/p2.png
r2=true
r3=true

Case: Tortoise rabbit race

public class Race implements Runnable{
    //winner
    private static String winner;

    @Override
    public void run() {
        for (int i = 1; i <= 20; i++) {
            //Simulated rabbit rest
            if (Thread.currentThread().getName().equals("rabbit")&&i%5==0){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            boolean flag=gameOver(i);
            if (flag){
                break;
            }
            System.out.println(Thread.currentThread().getName()+
                    "--->Run away"+i+"step");
        }
    }
    //Judge whether to complete the game
    public boolean gameOver(int steps){
        //Judge whether there is a winner
        if (winner!=null){
            return true;
        }
        if (steps>=20){
            winner=Thread.currentThread().getName();
            System.out.println("winner is "+winner);
            return true;
        }
        return false;
    }

    public static void main(String[] args) {
        Race race=new Race();
        new Thread(race,"rabbit").start();
        new Thread(race,"tortoise").start();
    }
}
//Output results:
rabbit--->Took a step
 rabbit--->Ran two steps
 rabbit--->Three steps
 rabbit--->Four steps
 tortoise--->Took a step
 tortoise--->Ran two steps
 tortoise--->Three steps
 tortoise--->Four steps
 tortoise--->Five steps
 tortoise--->Six steps
 tortoise--->Seven steps
 tortoise--->Eight steps
 tortoise--->Nine steps
 tortoise--->Ten steps
 tortoise--->Ran 11 steps
 tortoise--->12 steps
 tortoise--->13 steps
 tortoise--->Ran 14 steps
 tortoise--->Ran 15 steps
 tortoise--->16 steps
 tortoise--->17 steps
 tortoise--->Ran 18 steps
 tortoise--->Ran 19 steps
winner is tortoise

Static proxy

public class StaticProxy {
    public static void main(String[] args) {
        //This marriage case is equivalent to simulating thread static agent, which is equivalent to the following
        new WeddingCompany(new Man()).getMarry();
        
        new Thread(new Runnable() {
            @Override
            public void run() {
            }
        });
    }
}
interface Marry{
    void getMarry();
}
//Real role
class Man implements Marry{
    @Override
    public void getMarry() {
        System.out.println("Marry...");
    }
}
//delegable role
class WeddingCompany implements Marry{
    private Marry target;

    public WeddingCompany(Marry target) {
        this.target = target;
    }

    @Override
    public void getMarry() {
        before();
        target.getMarry();
        after();
    }
    private void before(){
        System.out.println("Before...");
    }
    private void after(){
        System.out.println("After...");
    }
}
//Output results:
Before...
Marry...
After...

Lambda expression

Functional interface

  • Any excuse that contains only one abstract method is a functional interface
  • For functional interfaces, we can create the interface object through Lambda expressions

Evolution of Lambda expressions

Create object call method

public class LambdaTest {
    public static void main(String[] args) {
        Tom tom=new Tom();
        tom.eatApple(3);
    }
}
interface Apple{
    void eatApple(int num);
}
class Tom implements Apple{

    @Override
    public void eatApple(int num) {
        System.out.println("Tom Yes"+ num + "An apple");
    }
}
//Output results:
Tom Ate three apples

Static inner class

public class LambdaTest {
  	//Static inner class
    static class Tom implements Apple{
        @Override
        public void eatApple(int num) {
            System.out.println("Tom Yes"+ num + "An apple");
        }
    }
    
    public static void main(String[] args) {
        Tom tom=new Tom();
        tom.eatApple(3);
    }
}
interface Apple{
    void eatApple(int num);
}
//Output results:
Tom Ate three apples

Local inner class

public class LambdaTest {
    public static void main(String[] args) {
      	//Local inner class
        class Tom implements Apple{
            @Override
            public void eatApple(int num) {
                System.out.println("Tom Yes"+ num + "An apple");
            }
        }
        Tom tom=new Tom();
        tom.eatApple(3);
    }
}
interface Apple{
    void eatApple(int num);
}
//Output results:
Tom Ate three apples

Anonymous Inner Class

public class LambdaTest {
    public static void main(String[] args) {
        Apple tomEatApple=new Apple() {
            @Override
            public void eatApple(int num) {
                System.out.println("Tom Yes"+ num + "An apple");
            }
        };
        tomEatApple.eatApple(3);
    }
}
interface Apple{
    void eatApple(int num);
}
//Output results:
Tom Ate three apples

Change to Lamda expression

  • Lambda expressions can only be simplified into one line when there is only one line of code. If there are multiple lines, they are wrapped in code blocks
  • The premise of Lambda expression is that the interface is functional
  • You can only remove the parameter type of multiple parameters. If you want to remove them, you must remove them all and add parentheses
public class LambdaTest {
    public static void main(String[] args) {
        Apple tomEatApple=(int num)-> {
           System.out.println("Tom Yes"+ num + "An apple");
        };
        //Simplified parameter type
        tomEatApple=(num)-> {
            System.out.println("Tom Yes"+ num + "An apple");
        };
        //Simplified parentheses
        tomEatApple=num-> {
            System.out.println("Tom Yes"+ num + "An apple");
        };
        //Simplified curly braces
        tomEatApple=num-> System.out.println("Tom Yes"+ num + "An apple");
        
        tomEatApple.eatApple(3);
    }
}
interface Apple{
    void eatApple(int num);
}
//Output results:
Tom Ate three apples

Thread state

Thread stop

  • The stop() and destroy() methods provided by JDK are not recommended [obsolete]
  • It is recommended that the thread stop by itself
  • It is recommended to use a flag bit to terminate the variable. When flag=false, the thread will be terminated
public class StopTest implements Runnable{
    private boolean flag=true;
    @Override
    public void run() {
        while(flag){
            System.out.println("run...");
        }
    }
    public void stop(){
        this.flag=false;
    }

    public static void main(String[] args) throws InterruptedException {
        StopTest stopTest = new StopTest();
        new Thread(stopTest).start();
        for (int i = 1; i <= 10; i++) {
            System.out.println("main"+i);
            if (i==5){
                stopTest.stop();
                System.out.println("run The thread is about to stop");
            }
        }
    }
}
//Output results:
main1
main2
main3
main4
main5
run...
run The thread is about to stop
main6
main7
main8
main9
main10

Thread sleep

  • Each object has a lock, and sleep will not release the lock
public class SleepTest {
    //1. Analog delay
    //2. Print the current system time
    public static void getNowTime() throws InterruptedException {
        Date startTime=new Date(System.currentTimeMillis());
        for (int i = 0; i < 5; i++) {
            Thread.sleep(1000);
            System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
            startTime=new Date(System.currentTimeMillis());
        }
    }
    //3. Analog countdown
    public static void timeDown() throws InterruptedException {
        int num=5;
        while (true){
            Thread.sleep(1000);
            System.out.println(num--);
            if (num<=0){
                break;
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        getNowTime();
        System.out.println("---");
        timeDown();
    }
}
//Output results:
11:10:44
11:10:45
11:10:46
11:10:47
11:10:48
---
5
4
3
2
1

Thread yield

//Source code
/**
     * A hint to the scheduler that the current thread is willing to yield
     * its current use of a processor. The scheduler is free to ignore this
     * hint.
     *
     * <p> Yield is a heuristic attempt to improve relative progression
     * between threads that would otherwise over-utilise a CPU. Its use
     * should be combined with detailed profiling and benchmarking to
     * ensure that it actually has the desired effect.
     *
     * <p> It is rarely appropriate to use this method. It may be useful
     * for debugging or testing purposes, where it may help to reproduce
     * bugs due to race conditions. It may also be useful when designing
     * concurrency control constructs such as the ones in the
     * {@link java.util.concurrent.locks} package.
     */
    public static native void yield();
  • When calling thread When yield (), it will give the thread scheduler a hint that the current thread is willing to sell CPU usage, but the thread scheduler may ignore this hint
public class YieldTest implements Runnable{
    public static void main(String[] args) {
        YieldTest yieldTest=new YieldTest();
        new Thread(yieldTest,"A").start();
        new Thread(yieldTest,"B").start();
    }
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("Current thread is: "+ Thread.currentThread().getName()+ i);
            if (i == 3){
                Thread.yield();
            }
        }
    }
}
//Output results:
//Case 1: after B3, give up the CPU, AB threads compete at the same time, and A obtains the CPU. Comity succeeds
 Current thread is: B0
 Current thread is: B1
 Current thread is: B2
 Current thread is: B3
 Current thread is: A0
 Current thread is: A1
 Current thread is: A2
 Current thread is: A3
 Current thread is: A4
 Current thread is: B4
//In case 2, A3 gives up the CPU, AB threads compete at the same time, and a obtains the CPU without comity
 Current thread is: A0
 Current thread is: A1
 Current thread is: A2
 Current thread is: A3
 Current thread is: A4
 Current thread is: B0
 Current thread is: B1
 Current thread is: B2
 Current thread is: B3
 Current thread is: B4

Thread enforces join

public class JoinTest implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("vip"+i+"...");
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(new JoinTest());
        thread.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("main"+i+"...");
            if (i==2){
                thread.join();
            }
        }
    }
}
//Output results:
main0...
main1...
main2...
vip0...
vip1...
vip2...
vip3...
vip4...
vip5...
vip6...
vip7...
vip8...
vip9...
main3...
main4...
main5...
main6...
main7...
main8...
main9...

Observe thread status

  • New: a new thread object is created, but the start() method has not been called yet

  • Runnable: the two states of ready and running in Java threads are generally called "running". After the thread object is created, other threads (such as the main thread) call the start() method of the object. The thread in this state is located in the runnable thread pool, waiting to be selected by thread scheduling to obtain the use right of CPU. At this time, it is in ready state. A thread in the ready state becomes running after obtaining the CPU time slice

  • Blocked: indicates that the thread is blocked in a lock

  • Waiting: the thread entering this state needs to wait for other threads to make some specific actions (notification or interrupt)

  • Terminated: indicates that the thread has completed execution

public class StateTest {
    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(()->{
            for (int i = 0; i < 3; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("In operation...");
            }
        });

        Thread.State state=thread.getState();
        //NEW
        System.out.println(state);
        //RUNNABLE
        thread.start();
        state=thread.getState();
        System.out.println(state);
        //TERMINATED
        while (state!= Thread.State.TERMINATED){
            Thread.sleep(100);
            state=thread.getState();
            System.out.println(state);
        }
    }
}
//Operation results:
NEW
RUNNABLE
TIMED_WAITING
 In operation...
TIMED_WAITING
 In operation...
TIMED_WAITING
 In operation...
TERMINATED

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
  • Low priority only means that the probability of obtaining scheduling is low, not that it will not be called if the priority is low, which depends on the CPU scheduling
  • Thread.MIN_PRIORITY = 1; Thread.MAX_PRIORITY = 10; Thread.NORM_PRIORITY = 5
public class PriorityTest implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getPriority());
    }
    public static void main(String[] args) {
        //Main thread priority
        System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getPriority());
        PriorityTest p=new PriorityTest();
        Thread t1 = new Thread(p);
        Thread t2 = new Thread(p);
        Thread t3 = new Thread(p);
        Thread t4 = new Thread(p);
        Thread t5 = new Thread(p);
        //Set priority before starting
        t1.start();

        t2.setPriority(Thread.MAX_PRIORITY);
        t2.start();

        t3.setPriority(Thread.MIN_PRIORITY);
        t3.start();

        t4.setPriority(7);
        t4.start();

        t5.setPriority(3);
        t5.start();
    }
}
//Output results:
main:5
Thread-1:10
Thread-4:3
Thread-3:7
Thread-2:1
Thread-0: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 complete execution, such as recording operation logs in the background, monitoring memory, garbage collection and so on
public class DaemonTest {
    public static void main(String[] args) {
        God god=new God();
        You you=new You();

        Thread thread=new Thread(god);
        thread.setDaemon(true);//By default, false is the user thread, and normal threads are user threads

        thread.start();
        new Thread(you).start();
    }
}
class God implements Runnable{
    @Override
    public void run() {
        while (true){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Guard you...");
        }
    }
}
class You implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if (i%3==0){
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("In life...");
        }
        System.out.println("End of life...");
    }
}
//Output results:
In life...
In life...
In life...
Guard you...
In life...
In life...
In life...
Guard you...
In life...
In life...
In life...
Guard you...
In life...
End of life...
Guard you...

Thread synchronization

  • Concurrency: the same object is operated by multiple threads at the same time

  • Thread synchronization: when dealing with multithreading, 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. Multiple threads that need to access this object at the same time enter the waiting pool of this object to form a queue, wait for the previous thread to use it, and then use it again for the next thread.

  • 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.

    • The following problems exist:
    • Holding a lock by one thread will cause all other threads that need the lock to hang
    • In multi-threaded 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

Synchronization method

public synchronized void method(int args) {}
  • Synchronized methods control access to "objects". Each object corresponds to a lock. Each synchronized method must obtain the lock of the object calling the method before execution, otherwise the thread will block. Once the method is executed, it will monopolize the lock until the method returns, and the lock will not be released until the blocked thread can obtain the lock and continue to execute.

Case: queuing to buy tickets

public class BuyTicket{
    public static void main(String[] args) {
        Ticket ticket=new Ticket();
        new Thread(ticket,"User 1").start();
        new Thread(ticket,"User 2").start();
        new Thread(ticket,"User 3").start();
    }
}
class Ticket implements Runnable{
    private int ticketNums=10;
    boolean flag= true;
    @Override
    public void run() {
        while (flag){
            try {
                Thread.sleep(1000);
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    private synchronized void buy() {
        if (ticketNums<=0){
            flag=false;
            return;
        }
        System.out.println(Thread.currentThread().getName()+"Bought a second"+(10-ticketNums+1)+"Ticket");
        ticketNums--;
    }
}
//Output results:
User 1 bought the first ticket
 User 2 bought the second ticket
 User 3 bought the third ticket
 User 1 bought the 4th ticket
 User 2 bought the fifth ticket
 User 3 bought the sixth ticket
 User 1 bought the 7th ticket
 User 2 bought the 8th ticket
 User 3 bought the 9th ticket
 User 1 bought the 10th ticket

Synchronization block

synchronized (Obj obj) {}
  • Obj is called synchronization monitor. OBJ can be any object, but it is recommended to use shared resources as synchronization monitor

  • 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

Case: bank withdrawal

public class GoBank {
    public static void main(String[] args) {
        Account account=new Account("Marry Money",100);
        Drawing dad=new Drawing(account,50,"dad");
        Drawing mom=new Drawing(account,100,"mom");
        dad.start();
        mom.start();
    }
}

//account
class Account{
    private String cardName;//Card name
    private int money;//balance
    public Account(String cardName, int money) {
        this.cardName = cardName;
        this.money = money;
    }
    public int getMoney() {
        return money;
    }
    public void setMoney(int money) {
        this.money = money;
    }
    public String getCardName() {
        return cardName;
    }
    public void setCardName(String cardName) {
        this.cardName = cardName;
    }
}

//bank
class Drawing extends Thread{
    private Account account;
    private int drawingMoney;
    private int nowMoney;
    private String name;

    public Drawing(Account account, int drawingMoney, String name) {
        this.account = account;
        this.drawingMoney = drawingMoney;
        this.name=name;
    }
    public void run(){
        synchronized (account){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //Judge whether there is money
            if (account.getMoney()-drawingMoney<0){
                System.out.println(name+"The money is not enough");
                return;
            }
            System.out.println(account.getCardName()+"by"+account.getMoney());
            account.setMoney(account.getMoney()-drawingMoney);
            nowMoney=nowMoney+drawingMoney;
            //Thread.currentThread().getName()=this.getName()
            System.out.println(name+"Money in hand"+nowMoney);
            System.out.println(account.getCardName()+"The balance is"+account.getMoney());
        }
    }
}
//Output results:
Marry Money Is 100
dad Money in hand 50
Marry Money The balance is 50
mom The money is not enough

Case: Security collection

//Method 1
public class SafeList {
    public static void main(String[] args) throws InterruptedException {
        List<String> list=new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()-> {
                synchronized (list) {
                    list.add(Thread.currentThread().getName());
                }
            }).start();
        }
        Thread.sleep(3000);
        System.out.println(list.size());
    }
}
//Output results:
10000
  
//Method 2 uses a set of security types designed by the system
public class SafeList {
    public static void main(String[] args) throws InterruptedException {
        CopyOnWriteArrayList<String> list=new CopyOnWriteArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()-> {
                list.add(Thread.currentThread().getName());
            }).start();
        }
        Thread.sleep(3000);
        System.out.println(list.size());
    }
}
//Output results:
10000

What is the lock object in the synchronization method or synchronization code block

  • For normal synchronization methods, the lock is the current instance object
  • For static synchronization methods, the lock is the Class object of the current Class
  • For synchronized method blocks, locks are objects configured in synchronized parentheses

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. Deadlock may occur when a synchronization block has locks of more than two objects at the same time.

Case: lipstick and mirror

public class DeadLock {
    public static void main(String[] args) {
        MakeUp girl1=new MakeUp(0,"girl1");
        MakeUp girl2=new MakeUp(1,"girl2");
        girl1.start();
        girl2.start();
    }
}
//Lipstick
class LipStick{

}
//mirror
class Mirror{

}

class MakeUp extends Thread{
    //static keyword to ensure a
    static LipStick lipStick=new LipStick();
    static Mirror mirror=new Mirror();

    int choice;
    String girlName;

    public MakeUp(int choice, String girlName) {
        this.choice = choice;
        this.girlName = girlName;
    }

    public void run(){
        //Make up
        try {
            makeUp();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void makeUp() throws InterruptedException {
        if (choice==0){
            //Get lipstick lock
            synchronized (lipStick){
                System.out.println(girlName+"Get lipstick lock");
                Thread.sleep(1000);
                //Get the lock of the mirror
                synchronized (mirror){
                    System.out.println(girlName+"Get the lock of the mirror");
                }
            }
        }else if (choice==1){
            //Get the lock of the mirror
            synchronized (mirror){
                System.out.println(girlName+"Get the lock of the mirror");
                Thread.sleep(2000);
                //Get lipstick lock
                synchronized (lipStick){
                    System.out.println(girlName+"Get lipstick lock");
                }
            }
        }
    }
}
//Output result: they all want to get each other's lock, resulting in being stuck here all the time
girl1 Get lipstick lock
girl2 Get the lock of the mirror

Lock lock

  • If there is an exception in the synchronization block, write unlock() to the finally statement block

Case: queuing to buy tickets

public class LockTest {
    public static void main(String[] args) {
        Ticket2 ticket=new Ticket2();
        new Thread(ticket,"User 1").start();
        new Thread(ticket,"User 2").start();
        new Thread(ticket,"User 3").start();
    }
}
class Ticket2 implements Runnable{
    //Define lock lock
    private final ReentrantLock lock=new ReentrantLock();
    private int ticketNums=10;
    boolean flag= true;
    @Override
    public void run() {
        while (flag){
            try {
                Thread.sleep(1000);
                //Lock
                lock.lock();
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                //Unlock
                lock.unlock();
            }
        }
    }
    private void buy() {
        if (ticketNums<=0){
            flag=false;
            return;
        }
        System.out.println(Thread.currentThread().getName()+"Bought a second"+(10-ticketNums+1)+"Ticket");
        ticketNums--;
    }
}
//Output result: analog synchronization method

Thread collaboration producer consumer model

  • Producers and consumers share the same resource, and producers and consumers are interdependent and conditional on each other
  • Java provides several methods to solve the communication problem between threads:
    • wait() means that the thread waits until other threads notify it. Unlike sleep, it will release the lock
    • wait(long timeout) specifies the number of milliseconds to wait
    • notify() wakes up a waiting thread
    • notifyAll() wakes up all threads calling the wait() method on the same object. Threads with higher priority are scheduled first
    • All methods of Object class can only be used in synchronization methods or synchronization code blocks, otherwise an exception IllegalMonitorStateException will be thrown

Pipe pass method using buffer zone

public class PCTest {
    public static void main(String[] args) throws InterruptedException {
        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;
    }
    public void run(){
        for (int i = 1; i <= 10; i++) {
            try {
                container.push(new Chicken(i));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Produced the second"+i+"Chicken");
        }
    }
}

//consumer
class Consumer extends Thread{
    SynContainer container;

    public Consumer(SynContainer container) {
        this.container = container;
    }
    public void run(){
        for (int i = 1; i <= 10; i++) {
            try {
                System.out.println("Consumed the second"+container.pop().id+"Chicken");
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

//product
class Chicken{
    int id;

    public Chicken(int id) {
        this.id = id;
    }
}

//buffer
class SynContainer{
    //A container size is required, that is, the buffer can only hold five chickens. Although the producer produces chickens again, they will be blocked later
    Chicken[] chickens=new Chicken[5];
    //Container counter
    int count=0;
    //The producer puts in the product
    public synchronized void push(Chicken chicken) throws InterruptedException {
        while (count==chickens.length){
            wait();//Producer waiting
        }
        //If it is not full, throw it into the product
        chickens[count]=chicken;
        count++;
        //Inform consumers of consumption
        notifyAll();
    }
    //Consumer products
    public synchronized Chicken pop() throws InterruptedException {
        Chicken thisChicken=null;
        //Judge whether it can be consumed
        while (count==0){
            wait();//Consumer waiting
        }
        //If you can consume
        count--;
        thisChicken = chickens[count];
        //When finished, inform the producer to produce
        notifyAll();
        return thisChicken;
    }
}
//Output results:
The first chicken was produced
 A second chicken was produced
 A third chicken was produced
 The fourth chicken was produced
 The fifth chicken was produced
 The fifth chicken was consumed
 The sixth chicken was produced
 The seventh chicken was produced
 Consumed the sixth chicken
 The eighth chicken was produced
 Consumed the seventh chicken
 Consumed the 8th chicken
 The ninth chicken was produced
 The 9th chicken was consumed
 The 10th chicken was produced
 Consumed the 10th chicken
 The fourth chicken was consumed
 Consumed the third chicken
 Consumed the second chicken
 Consumed the first chicken

Sign bit used by signal lamp method

public class PCTest2 {
    public static void main(String[] args) {
        TV tv=new TV();
        new Player(tv).start();
        new Watcher(tv).start();
    }
}
//Producer actor
class Player extends Thread{
    TV tv;
    public Player(TV tv) {
        this.tv = tv;
    }
    public void run(){
        for (int i = 0; i < 5; i++) {
            if (i%2==0){
                try {
                    tv.play("Program 1");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else {
                try {
                    tv.play("Program 2");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
//Consumer audience
class Watcher extends Thread{
    TV tv;
    public Watcher(TV tv) {
        this.tv = tv;
    }
    public void run(){
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(300);
                tv.watch();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
//Product program
class TV{
    String voice;
    boolean flag=true;
    //perform
    public synchronized void play(String voice) throws InterruptedException {
        if (!flag){
            wait();
        }
        System.out.println("The actors performed:"+voice);
        //Inform the audience to watch
        notifyAll();
        this.voice=voice;
        this.flag=!this.flag;
    }
    //watch
    public synchronized void watch() throws InterruptedException{
        if (flag){
            wait();
        }
        System.out.println("Watched:"+voice);
        //Inform the actors to perform
        notifyAll();
        this.flag=!this.flag;
    }
}
//Output results:
The actors performed: Program 1
 Watched: Program 1
 The actor performed: program 2
 Watched: program 2
 The actors performed: Program 1
 Watched: Program 1
 The actor performed: program 2
 Watched: program 2
 The actors performed: Program 1
 Watched: Program 1

Thread pool

  • JDK 5.0 provides thread pool related API s: ExecutorService and Executors

  • ExecutorService: the real thread pool interface. Common subclass ThreadPoolExecutor

    • void execute(Runnable command): executes a task / command without a return value. It is generally used to execute Runnable
    • < T > Future < T > submit (Callable < T > task): when a task is executed, it has a return value. Generally, Callable is executed again
    • void shutdown(): closes the connection pool
  • Executors: tool class and thread pool factory class, which are used to create and return different types of thread pools

//Taking the Runnable interface of the test thread pool as an example, Callable is mentioned above
public class PoolTest {
    public static void main(String[] args) {
        //Create service, create thread pool
        ExecutorService ex= Executors.newFixedThreadPool(2);
        //implement
        ex.execute(new MyThread());
        ex.execute(new MyThread());
        //Close connection
        ex.shutdown();
    }
}
class MyThread implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
    }
}
//Output results:
pool-1-thread-1 0
pool-1-thread-1 1
pool-1-thread-1 2
pool-1-thread-2 0
pool-1-thread-2 1
pool-1-thread-2 2

Hi, welcome to JasperのBlog!

Topics: Multithreading JavaSE thread pool