I. Introduction to threads

  • The programs running in the operating system are processes; QQ, games, etc. To put it bluntly, progress is the unit to which system resources are allocated to me.

  • A thread is a separate execution path;

  • When a thread is running, there will be multiple threads in the background, such as the main thread, gc thread, even if there is no thread created.

  • Main (), called the main thread, is the entry to the system and is used to execute the entire program;

  • In a process, if more than one thread is opened, the running of the thread is scheduled by the scheduler, the scheduler is closely related to the operating system, the order is not human intervention.

  • When performing operations on the same resource, resource grabbing may occur, and concurrency control must be added.

  • Threads introduce additional overhead, such as CPU scheduling time and concurrency control overhead.

  • Each thread interacts with its own working memory, and poor memory control can cause data inconsistencies

Second, thread creation

Method 1: Inherit the Thread class

The Thread class implements the Runnable interface.

Here we inherit the Thread class, rewrite its run method, create a Thread object, and call its start method

/** * Thread creation method 1: Create a class from Thread */
public class MyThread extends Thread{
    public void run(a){
        for (int i = 0; i < 500; i++) {
            System.out.println("I'm learning."); }}public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();

        for (int i = 0; i < 500; i++) {
            System.out.println("I'm working."); }}}Copy the code

Another case:

Implement multithreaded download operations: use commons.io. Jar to download resources from the network, and then write implementation classes

public class MyThread3 extends Thread{
    // File name and file url
    private String url;
    private String name;

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

    @Override
    public void run(a) {
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downloader(url,name);
        System.out.println("Downloaded file name" + name);
    }

    public static void main(String[] args) {
        MyThread3 myThread1 = new MyThread3("http://i0.hdslb.com/bfs/feed- admin/6f5c54330a8dafcc26fea7dbc822762e16ca63d4.jpg@880w_388h_1c_95q"."1.jpg");
        MyThread3 myThread2 = new MyThread3("http://i0.hdslb.com/bfs/feed-admin/6f5c54330a8dafcc26fea7dbc822762e16ca63d4.jpg@880w_388h_1c_95q"."2.jpg");
        MyThread3 myThread3 = new MyThread3("http://i0.hdslb.com/bfs/feed-admin/6f5c54330a8dafcc26fea7dbc822762e16ca63d4.jpg@880w_388h_1c_95q"."3.jpg"); myThread1.start(); myThread2.start(); myThread3.start(); }}class WebDownloader{
    public void downloader(String url,String name){
        // Copy the file
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO exception,downloader method error"); }}}Copy the code

Inheriting Thread classes is not recommended: OOP single inheritance is limited.

Method 2: Inherit the Runnable interface

public class MyThread2 implements Runnable{
    @Override
    public void run(a) {
        for (int i = 0; i < 300; i++) {
            System.out.println("I'm learning Java."); }}public static void main(String[] args) {
        MyThread2 myThread2 = new MyThread2();
        new Thread(myThread2).start();

        for (int i = 0; i <300; i++){
            System.out.println("I'm learning Python."); }}}Copy the code

Implement the Runnable interface, implement its run method, create a new thread to join the class object, and call the start method

Implementation of the Runnable interface: Recommended because it avoids the limitations of single inheritance and is flexible and convenient for the same object to be used by multiple threads.

3. Method 3: Implement callable interface

/** * Test the third way to create a thread: implement the Callable interface * Summary: * What to do? * * /
public class TestThread03 implements Callable<Boolean> {

    @Override
    public Boolean call(a) throws Exception {
        System.out.println("Added successfully");
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestThread03 iDontKnowWhat = new TestThread03();
        ExecutorService service = Executors.newFixedThreadPool(1);
        Future<Boolean> result = service.submit(iDontKnowWhat);

        booleanisTrue = result.get(); service.shutdownNow(); }}Copy the code

To achieve the case of downloading pictures

public class TestThread03_downloadPic implements Callable<Boolean> {
    private String url;
    private String name;

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

    @Override
    public Boolean call(a) throws Exception {
        DownloadPic3 downloadPic3 = new DownloadPic3();
        downloadPic3.download(url,name);
        System.out.println("What I print out is" + name);
        return true;
    }

    public static void main(String[] args) {
        TestThread03_downloadPic t1 = new TestThread03_downloadPic("https://i0.hdslb.com/bfs/sycp/creative_img/202103/8d14100881798762e1df65381e72a183.jpg@880w_388h_1c_95q"."Comic book 1. JPG");
        TestThread03_downloadPic t2 = new TestThread03_downloadPic("https://i0.hdslb.com/bfs/sycp/creative_img/202103/8d14100881798762e1df65381e72a183.jpg@880w_388h_1c_95q"."Comic book 2. JPG");
        TestThread03_downloadPic t3 = new TestThread03_downloadPic("https://i0.hdslb.com/bfs/sycp/creative_img/202103/8d14100881798762e1df65381e72a183.jpg@880w_388h_1c_95q"."Manga 3. JPG");

        ExecutorService callable = Executors.newFixedThreadPool(3); Future<Boolean> result1 = callable.submit(t1); Future<Boolean> result2 = callable.submit(t2); Future<Boolean> result3 = callable.submit(t3); }}class DownloadPic3{
    public void download(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch(IOException e) { e.printStackTrace(); }}}Copy the code

Introduction to static proxy

public class StaticProxy {
    public static void main(String[] args) {
        You you = new You();
        He he = new He();
        WeddingCompany weddingCompany1 = new WeddingCompany(you);
        WeddingCompany weddingCompany2 = newWeddingCompany(he); weddingCompany1.happyMerry(); weddingCompany2.happyMerry(); }}interface Merry{
    void happyMerry(a);
}

// Real characters
class You implements Merry{
    @Override
    public void happyMerry(a) {
        System.out.println("I'm married..."); }}class He implements Merry{
    @Override
    public void happyMerry(a) {
        System.out.println("He's married..."); }}// Proxy role
class WeddingCompany implements Merry{
    private Merry target;

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

    @Override
    public void happyMerry(a) {
        System.out.println("Wedding...");
        this.target.happyMerry();
        System.out.println("Bridal chamber..."); }} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- output -- the wedding... I'm married... Bridal chamber... The wedding... He's married... Bridal chamber...Copy the code

Static proxy pattern summary: Both the real object and the proxy object implement the same interface, and the proxy object calls the real object and its methods to extend. And proxy objects can do a lot of things that real objects can’t do, and real objects just have to focus on their own business.

Here the static proxy mode is a bit like implementing the Runnable interface to create multithreading:

new Thread( () --> System.out.println("something")).start();
new WeddingCompany(new You()).happyMerry();
Copy the code

4. Introduction to threads

Thread status

A thread has five states, as shown below:


2. Thread methods

Five, some common methods of thread

1. Stop the thread

The thread.stop() method is generally not recommended. Creating our own flag bits is recommended.

/** * here tests the method to stop a thread * recommended normal thread stop -> number of times, not recommended infinite loop * recommended to use flag bit -> set flag bit * best not to use stop or destroy and other JDK deprecated methods */
public class ThreadStop implements Runnable{

    // Set an identifier bit
    private boolean flag = true;

    @Override
    public void run(a) {
        int i = 0;
        while (flag){
            System.out.println("run... Thread"+ i++); }}// Set a public method to stop the thread
    public void stop(a){
        this.flag = false;
    }
    public static void main(String[] args) {
        ThreadStop threadStop = new ThreadStop();
        new Thread(threadStop).start();

        for (int i = 0; i < 1000; i++) {
            System.out.println("The main thread executes" + i);
            if (i == 900) {// Stop the thread
                threadStop.stop();
                System.out.println("Thread should stop...."); }}}}Copy the code

Here we start two threads, the main thread and the slave thread. After executing the thread stop method, our slave thread stops

2. Thread sleep

  • Sleep (milliseconds) specifies the practice for the current thread to stop
  • Exception InteruptedException exists in sleep()
  • The thread enters the ready state after the sleep() practice arrives
  • Sleep () can simulate network delay, countdown, etc
  • Every object has a lock, and sleep does not release the lock
/** * Test network delay, simulate network delay may occur */public class ThreadSleep implements Runnable{    private Integer ticketNum = 10;    @Override    public void run(a) {        while (true) {if (ticketNum <= 0) {break;            }            try {                Thread.sleep(500);            } catch (InterruptedException e) {                e.printStackTrace();            }            System.out.println(Thread.currentThread().getName() + "Got the number one." + ticketNum-- + "Ticket"); }}public static void main(String[] args) {        ThreadSleep threadSleep = new ThreadSleep();        new Thread(threadSleep,"Cattle 1").start();        new Thread(threadSleep,"Scalpers 2").start();        new Thread(threadSleep,"Scalpers 3").start();    }}
Copy the code

The example above simulates network latency, in which concurrency creates the possibility of multiple people manipulating an object, making threads unsafe.

Test the countdown and timer

public class ThreadSleep2 implements Runnable{    private Integer time = 10;    @Override    public void run(a) {        while (time >= 0) {try {                Thread.sleep(1000);            } catch (InterruptedException e) {                e.printStackTrace();            }            System.out.println("What time is it now?"+ time--); }}public static void main(String[] args) {        Date nowTime = new Date(System.currentTimeMillis());While (true){try {thread.sleep (1000); System.out.println(new SimpleDateFormat("HH:mm:ss:SSS").format(nowTime)); nowTime = new Date(System.currentTimeMillis()); } catch (InterruptedException e) { e.printStackTrace(); }}}}
Copy the code

Thread comity

  • Yield to the thread by suspending, but not blocking, the currently executing thread
  • Moves a thread from the running state to the ready state
  • Let CPU rescheduling, comity is not necessarily successful, see CPU mood
public class ThreadYield{    public static void main(String[] args) {        MyYield myYield = new MyYield();        new Thread(myYield,"A").start();        new Thread(myYield,"B").start();;    }}class MyYield implements Runnable{    @Override    public void run(a) {        System.out.println(Thread.currentThread().getName() +"Thread starts executing");        Thread.yield();        System.out.println(Thread.currentThread().getName() +"Thread terminates execution");    }}
Copy the code

Thread cutting in line

  • Join Joins a thread. After this thread completes, other threads are executing, and other threads are blocking
  • Think of it as cutting in line
public class ThreadJoin implements Runnable{    @Override    public void run(a) {        for (int i = 0; i < 1000; i++) {            System.out.println("Thread VIP is here."+ i); }}public static void main(String[] args) {        ThreadJoin threadJoin = new ThreadJoin();        Thread thread = new Thread(threadJoin);        thread.start();        for (int i = 0; i < 1000; i++) {            if (i == 200) {try {                    thread.join();                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            System.out.println("Here comes the thread."+ i); }}}Copy the code

This way, when the main thread reaches 200, the thread cuts in line until it finishes executing the main method

5. Observe thread status

public class ThreadState {    public static void main(String[] args) throws InterruptedException {        Thread thread = new Thread(()->{            for (int i = 0; i < 5; i++) {                try {                    Thread.sleep(1000);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            System.out.println("= = = = = = = = = = = = = = = = = =");        });        Thread.state State = thread.getState(); System.out.println(state); Thread.start (); state = thread.getState(); System.out.println(state); while (state ! = Thread.State.TERMINATED){ thread.sleep(100); state = thread.getState(); System.out.println(state); }}}
Copy the code

6. Thread priority

  • Java provides a thread scheduler to monitor all threads in a program that are ready to start; The thread scheduler determines which thread to schedule execution based on priority. The priority of the thread is expressed numerically, ranging from 1 to 10
  • Thread.MIN_PRIORITY = 1
  • Thread.MAX_PRIORITY= 10
  • Thread.NORM_PRIORITY = 5
  • Use getPriority() and setPriority() to get or change the priority
public class ThreadPriority{    public static void main(String[] args) throws InterruptedException {        / / the main - > 5 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - > the default priority System. The main Thread out.println (Thread. The currentThread (). The getName () + "-- -- >" + Thread.currentThread().getPriority()); MyPrority myPrority = new MyPrority(); Thread thread1 = new Thread(myPrority); Thread thread2 = new Thread(myPrority); Thread thread3 = new Thread(myPrority); Thread thread4 = new Thread(myPrority); Thread thread5 = new Thread(myPrority); Thread thread6 = new Thread(myPrority); thread1.setPriority(5); thread1.start(); thread2.setPriority(4); thread2.start(); thread3.setPriority(7); thread3.start(); thread4.setPriority(10); thread4.start(); thread5.setPriority(3); thread5.start(); thread6.setPriority(1); Thread.sleep(1000); thread6.start(); }}class MyPrority implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()  + "-->" + Thread.currentThread().getPriority()); }}
Copy the code

Daemon threads

  • Threads are divided into user threads and daemon threads
  • The virtual machine must ensure that the user thread completes execution
  • The virtual machine does not wait for the daemon thread to complete execution
  • Such as background operation log, memory monitoring, garbage collection
Public class ThreadDemo {public static void main(String[] args) {God = new God(); You you = new You(); Thread thread = new Thread(god); thread.setDaemon(true); //false indicates the user thread, true indicates the daemon thread thread.start(); Thread1 = new Thread(you); thread1.start(); }// implements Runnable{@override public void run() {while (true){system.out.println (" implements Runnable "); }}// implements Runnable{@override public void run() {for (int I = 0; i < 36500; I++) {system.out.println (" you have been happy all your life or "); } System.out.println("goodbye! world!" ); }}
Copy the code

This means that when the user thread finishes executing and the virtual machine shuts down, the daemon thread is shut down. It still exists, but the system will terminate the daemon thread as soon as the user thread finishes executing

4. Thread synchronization

Concurrency: The same object is operated on simultaneously by more than one thread

  • When dealing with multiple threads, multiple threads access the same object, and some also want to modify this thread object, by this time we need to thread synchronization, thread synchronization is actually a kind of wait for mechanism, multiple threads need to access the object at the same time into the object of waiting pool forming a queue, waiting for the thread after use, in front of a thread is in use
  • Due to the same process of multiple threads share the same piece of storage space, in bring convenient while, also brought the access conflicts, in order to ensure the correctness of the data to be accessed in the method, when access to join syncronized lock mechanism, when a thread to acquire the exclusive lock objects, exclusive resources, other threads must wait, after releasing the lock can be used, save In the following questions
    • Holding a lock by one thread causes all other processes requiring the lock to suspend
    • In the case of multi-thread competition, locking, locking release will lead to more context switching and scheduling delay, resulting in performance problems
    • If a high-priority thread waits for a low-priority thread to release the lock, priority inversion can occur, causing performance problems

1. Unsafe cases

Unsafe buying tickets

public class UnsafeBuyTicket {    public static void main(String[] args) {        BuyTicket buyTicket = new BuyTicket();        new Thread(buyTicket,"Cattle 1").start();        new Thread(buyTicket,"Scalpers 2").start();        new Thread(buyTicket,"Scalpers 3").start();    }}class BuyTicket implements Runnable{    private int ticketNum = 10;    private boolean flag = true;@override public void run() {while (flag){buyTicket(); } } private void buyTicket() { if (ticketNum <= 0){ flag = false; return; } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } system.out.println (thread.currentThread ().getName() + "buy" + ticketNum--); }}
Copy the code

Unsafe withdrawal of money

public class UnsafeBank {    public static void main(String[] args) {        Account account = new Account(1000000."Marriage Fund");        Drawing you = new Drawing(account,500000."You");        Drawing yourWife = new Drawing(account,1000000."Your wife.");        you.start();        yourWife.start();    }}// Account class Account{int money; // Balance String name; Public Account(int money, String name) {this.money = money; this.name = name; }}// extends Thread{Account; Int drawingMoney; // How much money do you have now? public Drawing(Account account,int drawingMoney,String name){ super(name); this.account = account; this.drawingMoney = drawingMoney; } @Override public void run() { if (account.money - drawingMoney < 0){ System.out.println(thread.currentThread ().getName()); return; } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } account.money = account.money - drawingMoney; nowMoney = nowMoney + drawingMoney; System.out.println(account.name + "balance =" + account.money + "); System.out.println(this.getName() + "in hand" + "nowMoney "); }}
Copy the code

Thread-unsafe collection

public class UnsafeList {    public static void main(String[] args) {        List<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());    }}
Copy the code

2. Synchronization method and synchronization block

Since we can use the private keyword to ensure that data objects can only be accessed by methods, we need to propose only one mechanism for methods, the synchronized keyword, which has two uses :synchronized methods and synchronized blocks

Public synchronized void method(int args)}

Synchronized methods control access to “objects”. Each object corresponds to a lock. Each synchronized method must acquire the lock of the object calling the method to execute, otherwise the thread will block. Once executed, the method monopolizes the lock and does not release it until the method returns, allowing subsequent blocked threads to acquire the lock and continue execution.

Pitfalls: Declaring a large method as synchronized can affect efficiency

Methods that need to be modified need to be locked; otherwise, resources are wasted.

In these cases, you can add the synchronized modifier to solve the thread synchronization problem.

public class UnsafeBuyTicket {    public static void main(String[] args) {        BuyTicket buyTicket = new BuyTicket();        new Thread(buyTicket,"Cattle 1").start();        new Thread(buyTicket,"Scalpers 2").start();        new Thread(buyTicket,"Scalpers 3").start();    }}class BuyTicket implements Runnable{    private int ticketNum = 10;    private boolean flag = true;@override public void run() {while (flag){try {thread.sleep (1000); } catch (InterruptedException e) { e.printStackTrace(); } buyTicket(); }} private synchronized void buyTicket() {if (ticketNum <= 0){flag = false; return; } system.out.println (thread.currentThread ().getName() + "buy" + ticketNum--); }}
Copy the code

In the second case, you add the synchronized modifier to the run() method and find that it still doesn’t work.

Since this represents the bank, we should not lock this object. Use synchronized blocks instead. Synchronized locks the current this by default.

  • 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 a synchronous method, because the synchronization monitor in a synchronous method is either this, the object itself, or class
  • Synchronize the execution of the monitor

    • The first thread accesses the synchronization monitor, locks it, and executes the code
    • The second thread visits and finds that the synchronization monitor is locked and cannot be accessed
    • The synchronization monitor is unlocked after the first thread accesses
    • The second thread accesses, finds that the synchronization monitor is not locked, and then locks and accesses
  • Methods add synchronized keys to methods that lock the object itself

The code can be changed to the following. Previously, the bank was locked, but the bank is public. Here, we lock the account with synchronized. We lock the object should be to add, delete, change and check the public resources.

public class UnsafeBank {    public static void main(String[] args) {        Account account = new Account(1000000."Marriage Fund");        Drawing you = new Drawing(account,500000."You");        Drawing yourWife = new Drawing(account,1000000."Your wife.");        you.start();        yourWife.start();    }}// Account class Account{int money; // Balance String name; Public Account(int money, String name) {this.money = money; this.name = name; }}// extends Thread{Account; Int drawingMoney; // How much money do you have now? public Drawing(Account account,int drawingMoney,String name){ super(name); this.account = account; this.drawingMoney = drawingMoney; } @Override public void run() { synchronized (account){ if (account.money - drawingMoney < 0){ System.out.println(thread.currentThread ().getName()); return; } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } account.money = account.money - drawingMoney; nowMoney = nowMoney + drawingMoney; System.out.println(account.name + "balance =" + account.money + "); System.out.println(this.getName() + "in hand" + "nowMoney "); }}}
Copy the code

3. Preliminary JUC

Here is a brief introduction to CopyOnWriteArrayList in JUC.

Public class TestJUC {public static void main(String[] args) {CopyOnWriteArrayList
      
        list = new CopyOnWriteArrayList<>(); 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()); }}
      
Copy the code

Look at the source code: there is a constructor:

public CopyOnWriteArrayList(a) {    setArray(new Object[0]); }Copy the code

The array source code has two modifiers: TRANSIENT /volatile

private transient volatile Object[] array;
Copy the code

3, a deadlock

A situation in which two or more threads stop executing while each thread is waiting for the other thread to release the shared resource. Deadlock problems can occur when a synchronized block has “locks on more than two objects”.

Public class DeadLock {public static void main(String[] args) {Makeup g1 = new Makeup(0," Cinderella "); Makeup G2 = new Makeup(1," Snow White "); g1.start(); g2.start(); }} class LipStick{} class Makeup extends Thread{static LipStick = new LipStick(); static Mirror mirror = new Mirror(); int choice; String girlName; Makeup(int choice,String girlName){ this.choice = choice; this.girlName = girlName; } @override public void run() {makeup {makeup(); } catch (InterruptedException e) { e.printStackTrace(); }} private void makeup() throws InterruptedException {if (choice == 0){synchronized (lipStick) System.out.println(this.girlname + "get lipstick lock "); Thread.sleep(1000); Synchronized (mirror){system.out.println (this.girlname + "obtain mirror lock "); synchronized (mirror){system.out.println (this.girlname +" obtain mirror lock "); }}}else{synchronized (mirror){system.out.println (this.girlname + "obtain mirror lock "); Thread.sleep(2000); Synchronized (lipStick){system.out.println (this.girlname + "obtain lipStick lock "); }}}}}
Copy the code

How to solve it? Just take them out.

public class DeadLock {    public static void main(String[] args) {        Makeup g1 = new Makeup(0.Cinderella);        Makeup g2 = new Makeup(1.Snow White);        g1.start();        g2.start();    }}Class LipStick{} class Makeup extends Thread{}class LipStick = new LipStick(); static Mirror mirror = new Mirror(); int choice; String girlName; Makeup(int choice,String girlName){ this.choice = choice; this.girlName = girlName; } @override public void run() {makeup {makeup(); } catch (InterruptedException e) { e.printStackTrace(); }} private void makeup() throws InterruptedException {if (choice == 0){synchronized (lipStick) System.out.println(this.girlname + "get lipstick lock "); Thread.sleep(1000); } synchronized (mirror){system.out.println (this.girlname + "obtain mirror lock "); }}else{synchronized (mirror){system.out.println (this.girlname + "obtain mirror lock "); Thread.sleep(2000); } synchronized (lipStick){system.out. Println (this.girlname + "obtain lipStick lock "); }}}}
Copy the code

4, the Lock locks

  • Since JDK 5.0, Java has provided a more powerful thread synchronization mechanism _ — synchronization can be achieved by explicitly defining a synchronization lock object. A synchronous Lock uses the Lock object to act as a Lock;
  • Java. Util. Concurrent. The locks. Lock interface is the tool of control multiple threads to Shared resources access. A Lock provides exclusive access to a shared resource. Only one thread at a time can Lock the Lock object. The thread must acquire the Lock object before starting to access the shared resource
  • The ReentrantLock class (ReentrantLock) implements Lock, which has the same concurrency and memory semantics as synchronized. In implementing thread-safe control, ReentrantLock is commonly used to explicitly Lock and release locks.

Again, the previous example:

Public class TestLock {public static void main(String[] args) {TestLock2 TestLock2 = new TestLock2(); new Thread(testLock2).start(); new Thread(testLock2).start(); new Thread(testLock2).start(); }}class TestLock2 implements Runnable{ int ticketNums = 10; @Override public void run() { while (true){ if (ticketNums > 0){ try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(ticketNums--); }else{ break; }}}}
Copy the code

This can be problematic, and the ticket may turn negative. Use Reentrantlock to lock:

Public class TestLock {public static void main(String[] args) {TestLock2 TestLock2 = new TestLock2(); new Thread(testLock2).start(); new Thread(testLock2).start(); new Thread(testLock2).start(); }}class TestLock2 implements Runnable{ int ticketNums = 10; Private final ReentrantLock Lock = new ReentrantLock(); @Override public void run() { while (true){ try { lock.lock(); Thread.sleep(50); if (ticketNums > 0) {try {thread.sleep (50); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(ticketNums--); } else { break; } }finally { lock.unlock(); }}}}
Copy the code

Synchronized vs Lock

  • Lock is an explicit Lock (manually open and close the Lock, do not forget to close the Lock) synchronized is an implicit Lock, automatically released out of the scope
  • Lock has only code block locks, synchronized has code block locks and method locks
  • With Lock locks, the JVM takes less time to schedule threads and performs better. And more extensible (more subclasses)
  • Priority order: Lock > Synchronize code block (already in method body, allocated resources) > synchronize method (outside method body)

5. Thread collaboration

1. Producer-consumer model

1) Application scenario: producer and consumer issues

  • Assuming that only – – products can be stored in the warehouse, the producer will produce the products into the warehouse, the consumer will take away the products in the warehouse for consumption.
  • If there is no product in the warehouse, the producer puts the product in the warehouse, otherwise stops production and waits until the product in the warehouse is picked up by the consumer.
  • If there is a product in the warehouse, the consumer can take it away for consumption, otherwise stop consuming and wait until the product is put in the warehouse again.

2) analysis

This is a thread synchronization problem, producers and consumers share the same resource, and producers and consumers depend on each other and condition each other.

  • For producers, consumers should be informed to wait before producing the product. After the product is produced, consumers need to be notified immediately for consumption
  • For consumers, after consumption, producers should be notified that they have finished consumption and need to produce new products for consumption.
  • Synchronized is not enough in producer-consumer problems
  • Synchronized prevents concurrent updates of a shared resource and implements synchronization
  • Synchronized cannot be used for messaging between different threads.

Java provides several ways to solve the problem of communication between threads:

2. The first solution is the pipe process method

/** * Test: producer-consumer model --> Use buffer solution: pipe method */public class Demo33_ThreadPC {    public static void main(String[] args) {        SynContainer synContainer = new SynContainer();        new Producer(synContainer).start();        new Consumer(synContainer).start();    }}// Producer extends Thread {// Producer extends Thread; public Producer(SynContainer container) { this.container = container; } public void run() {for (int I = 0; i < 100; i++) { container.push(new Product(i)); System.out.println(" produced "+ I +" produced "); }}// Consumer extends Thread {// SynContainer container; public Consumer(SynContainer container) { this.container = container; } public void run() {for (int I = 0; i < 100; I++) {System. Out. Println (" consumption -- -- > "+ container. The pop (). The id +" product "); }}// Product class Product {int id; Public Product(int id) {this.id = id; Product[] products = new Product[10]; Int count = 0; Public synchronized void push(Product Product) {synchronized void push(Product Product) {synchronized void push(Product Product); So the index goes to 0 and the release lock is held by consumer 2 instead of the producer, and the consumer's wait is in if so it goes out of bounds with index-1, */ while (count == products.length) {try {this.wait(); } catch (InterruptedException e) { e.printStackTrace(); Product [count] = product; count++; // Notify the consumer to consume this.notifyall (); } public synchronized Product pop() {while (count <= 0) {this.wait(); } catch (InterruptedException e) { e.printStackTrace(); }} // If you can consume count--; Product product = products[count]; This.notifyall (); return product; }}
Copy the code

3. Signal light method of solution two

/** * Test: producer-consumer model --> Use buffer solution: pipe method */public class Demo33_ThreadPC {    public static void main(String[] args) {        SynContainer synContainer = new SynContainer();        new Producer(synContainer).start();        new Consumer(synContainer).start();    }}// Producer extends Thread {// Producer extends Thread; public Producer(SynContainer container) { this.container = container; } public void run() {for (int I = 0; i < 100; i++) { container.push(new Product(i)); System.out.println(" produced "+ I +" produced "); }}// Consumer extends Thread {// SynContainer container; public Consumer(SynContainer container) { this.container = container; } public void run() {for (int I = 0; i < 100; I++) {System. Out. Println (" consumption -- -- > "+ container. The pop (). The id +" product "); }}// Product class Product {int id; Public Product(int id) {this.id = id; Product[] products = new Product[10]; Int count = 0; Public synchronized void push(Product Product) {synchronized void push(Product Product) {synchronized void push(Product Product); So the index goes to 0 and the release lock is held by consumer 2 instead of the producer, and the consumer's wait is in if so it goes out of bounds with index-1, */ while (count == products.length) {try {this.wait(); } catch (InterruptedException e) { e.printStackTrace(); Product [count] = product; count++; // Notify the consumer to consume this.notifyall (); } public synchronized Product pop() {while (count <= 0) {this.wait(); } catch (InterruptedException e) { e.printStackTrace(); }} // If you can consume count--; Product product = products[count]; This.notifyall (); return product; }}
Copy the code

4. Thread pools

Using thread pools

Background: Heavily used resources that are frequently created and destroyed, such as threads in concurrent situations, have a significant impact on performance.

Create several threads in advance and put them into the thread pool. It can avoid frequent creation and destruction and realize reuse. Similar to public transportation in life.

  • Benefits: Improved response time (less time to create new threads) Reduced resource consumption (reuse of threads in the thread pool, not need to be created every time) easy thread management…

    • CorePoolSize: Size of the core pool
    • MaximumPoolSize: indicates the maximum number of threads
    • KeepAliveTime: The maximum amount of time a thread can hold without a task before terminating
  • JDK 5.0 provides thread pool-related apis: ExecutorService and Executors

  • ExecutorService: A true thread pool interface. ThreadPoolExecutor void execute(Runnable command) : Executes a task/command without returning a value. Runnable Future Submit (Callable Task): executes a task and returns a value. Callable void shutdown() : shuts down the connection pool

  • Executors: Factory classes for tools and thread pools, used to create and return different types of thread pools

public class TestPool {    public static void main(String[] args) {        / / 1. Create a service, create a thread pool ExecutorService service = Executors. NewFixedThreadPool (10); 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()); }}
Copy the code