Understand Java multithreading in one article

Java multi-threading operation mechanism

Creation of Java multithreading

There are three ways to create Java multi-threads:
1. Create a process by inheriting the Thread class
2. Create a thread through the Runnable interface
3. Create a thread through the Callable interface and Future interface

1. Create a process by inheriting the Thread class

  • Step 1: Create a subclass, rewrite run()the method of the class, and implement the function of the thread
  • Step 2: Create an instance of the Thread subclass, that is, create a process object
  • Step 3: Call start()the method of the thread object to start the thread

Thread common methods

method illustrate
void run() The code executed when the thread is running
void interript Interrupt process
void start() Cause the thread to start executing
static void yield() Pause the currently executing process and execute other processes
Thread.State getState() Returns the status of this thread
final boolean isAlive() Test whether the thread is active
String getName() Returns the name of this thread
void setName(String name) Change thread name
public class www {
    
    
    public static void main(String[] args){
    
    
        for (int i = 0; i < 3; i++) {
    
    
            Test test = new Test(i);
            test.start();
        }
    }
}
class Test extends Thread{
    
    
    int name;
    public Test(int name){
    
    
        this.name = name;
    }
    public void run() {
    
    
        System.out.println("线程"+name);
    }
}

Insert image description here

The output results may be different each time because the execution progress of the sub-threads is uncertain and they run concurrently.

Disadvantage: If the class already inherits a class, it cannot inherit Threadthe class.

2. Create threads through Runnable interface

  • Step 1: Define Runnablethe implementation class of the interface and implement the methods of the interfacerun()
  • Step 2: Define Runnablean instance of the implementation class, and use this instance as a parameter Threadof the class targetto create Threada thread object. This Threadobject is the real thread object.
public class www {
    
    
    public static void main(String[] args){
    
    
        for (int i = 0; i < 3; i++) {
    
    
            Test test = new Test(i);
            Thread t = new Thread(test);
            t.start();
        }
    }
}
class Test implements Runnable{
    
    
    int name;
    public Test(int name){
    
    
        this.name = name;
    }
    public void run() {
    
    
        System.out.println("线程"+name);
    }
}

Insert image description here

Similarly, the output results may be different each time. This is also because the execution progress of the sub-threads is uncertain and they run concurrently.

3. Create threads through Callable interface and Future interface

  • Step 1: Create Callablean implementation class of the interface and implement call()the method, which will serve as the execution body of the thread and have a return value.
  • Step 2: Create Callablean instance of the implementation class and use FutureTaskthe class to wrap Callablethe object that FutureTaskencapsulates the return value of the Callableobject's call()method.
  • Step 3: Using FutureTaskthe object as Threadobject target, create and start a new thread.
  • Step 4: Call the method FutureTaskof the object get()to obtain the return value when the sub-thread execution ends.
public class www {
    
    
    public static void main(String[] args){
    
    
        for (int i = 0 ; i < 3 ; i++){
    
    
            Test test = new Test(i);
            //使用FutureTask来包装Callable对象
            FutureTask result = new FutureTask(test);
            //创建线程对象
            Thread thread = new Thread(result);
            //启动线程
            thread.start();
        }
    }
}
class Test implements Callable{
    
    
    int name;
    public Test(int name){
    
    
        this.name = name;
    }
    @Override
    public Object call() throws Exception {
    
    
        System.out.println("线程"+name);
        return null;
    }
}

Insert image description here

Similarly, the output results may be different each time. This is also because the execution progress of the sub-threads is uncertain and they run concurrently.

Java thread life cycle

It is divided into six states, specifically the creation (New) state, the runnable (Runnable) state, the blocked (Blocked) state (Bu Lao Kete), the waiting state (Waiting) state, the timed waiting state (Timed waiting) state, and the terminated (Terminated) state. ) status.

Java thread scheduling

Thread sleep——Sleep

You can pause the currently executing thread for a period of time

public class www extends JFrame implements Runnable {
    
    
    JLabel jLabel1,jLabel2;
    public www (){
    
    
        jLabel1 = new JLabel("当前时间:");
        jLabel2 = new JLabel();
        Container containerPane = this.getContentPane();
        containerPane.setLayout(new FlowLayout());
        this.add(jLabel1);
        this.add(jLabel2);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setSize(300,200);
        this.setVisible(true);
    }
    @Override
    public void run() {
    
    
        while (true){
    
    
            jLabel2.setText(getTime());
            //获取当前进程 并延时2000ms
            try {
    
    
                Thread.currentThread().sleep(2000);
            } catch (InterruptedException e) {
    
    
                throw new RuntimeException(e);
            }
        }
    }
    String getTime(){
    
    
        Date date =new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日HH:mm:ss Z");
        return simpleDateFormat.format(date);
    }
    public static void main(String[] args) {
    
    
        www test1 = new www();
        Thread thread1 = new Thread(test1);
        thread1.start();
    }
}

Insert image description here

It can be seen that the time is refreshed every 2S (2000ms)

Thread concession - yield (youde)

yield()Both sleep()will suspend the execution of the current process. The difference is that it yield()will first determine whether there is a process with the same or higher priority than the process. If there is, it will stop and let the same or higher priority process run first. If there is no process, the process will continue to run.

public class www  implements Runnable {
    
    
    String str = "";
    @Override
    public void run() {
    
    
        for (int i = 1; i <= 9; i++) {
    
    
            str += Thread.currentThread().getName() + "----" + i + "     ";
            if(i%3 == 0){
    
    
                System.out.println(str);
                str = "";
                Thread.currentThread().yield();
            }
        }
    }
    public static void main(String[] args) {
    
    
        www test1 = new www();
        www test2 = new www();
        Thread thread1 = new Thread(test1,"线程1");
        Thread thread2 = new Thread(test2,"线程2");
        thread1.start();
        thread2.start();
    }
}

Insert image description here

The output result threads are alternate, and the output results may not be alternate and different every time it is run, so the execution method of controlling the thread through yield() is unreliable.

Thread collaboration-join

If a thread reaches a certain point and waits for another thread to finish before it can continue to run, this situation can be achieved by calling the join() method of another thread.

public class www {
    
    
    public static void main(String[] args) {
    
    
        Thread thread = new test();
        thread.start();
        for (int i = 1; i <= 10; i++) {
    
    
            System.out.println("主线程");
            if(i == 5){
    
    
                try {
    
    
                    thread.join();
                } catch (InterruptedException e) {
    
    
                    throw new RuntimeException(e);
                }
            }
        }
    }
}
class test  extends Thread{
    
    
    public void run(){
    
    
        for (int i = 1; i <= 10 ; i++) {
    
    
            System.out.println("子线程");
        }
    }
}

Insert image description here

join()Normally, the main thread creates and starts the thread. If the sub-thread takes a lot of time to run, the main thread often finishes running earlier than the sub-thread. This example is to set the status to wait for the sub-thread to run before the main thread finishes running. Finish and then continue running.

process priority

Each process in Java has a corresponding priority, and those with higher priorities get more running opportunities. Thread priority is usually represented by a number between 1 and 10. The larger the number, the higher the priority. The default thread level is 5.

Three constants are defined in the Thread class

name value
MIN_PRIORITY 1
MAX_PRIORITY 10
NORM_PRIORITY 5

Priority setting setPrioriy()methods and methods of obtaining prioritygetPrioriy()

daemon

Java is divided into two categories 用户线程and守护线程

Daemon threads are also called background threads that provide services to other threads. Such threads can monitor the running dependencies of other threads and other threads.

The user thread is a general thread responsible for business logic

You can use setDaemon()the method to set the type of a thread to true. The daemon thread can only 线程.start()be called before. You can also use isDaemon()the method to determine whether the process is a daemon process.

Thread synchronization

Problems caused by multi-threading: When designing multi-threading, sometimes multiple threads need to share a part of the code block to achieve the purpose of sharing a private member variable or static member of the class. At this time, because threads compete with each other for CPU resources, threads access these shared resources out of order, and ultimately correct results may not be obtained. These problems are often called thread safety problems.

For example:

public class www {
    
    
    public static void main(String[] args) {
    
    
        test test = new test();
        for (int i = 0; i < 10; i++) {
    
    
            new Thread(test).start();
        }
    }
}
class test  implements Runnable{
    
    
    public int num = 0;
    //使用temp是为了增加线程的切换几率
    private void add(){
    
    
        int temp;
        for (int i = 0; i < 1000; i++) {
    
    
            temp = num;
            temp++;
            num  = temp;
        }
        System.out.println(Thread.currentThread().getName() + "  "+ num);
    }
    public void run(){
    
    
        add();
    }
}

Insert image description here

The output results are almost never correct in multiples of 1000. This is due to the concurrent execution of multi-threads and the simultaneous nummodification of variables by multiple threads. To solve this problem, Java's synchronization mechanism is necessary.

synchronized code block

Java is equipped with a lock and a waiting set for each object. This object can be an instance object or a class object. Locking the instance object can ensure that threads related to the instance object can use the object lock mutually exclusive; locking the class object can ensure that the threads related to the class can use the class object lock mutually. Create an instance object through the new keyword to obtain a reference to the object. To obtain a reference to the class object. We can pass forNamemember methods. The static member variables and static member methods of a class belong to the class object, while the non-static member variables and non-static member methods of a class belong to the instance object of the class.

synchronize(synObject){
    
    
    //关键code
}

When the process enters the critical code, the system will first check whether the object's lock has been acquired by other threads. If not, the JVM will hand over the object's lock to the process currently requesting the lock. After the thread acquires the lock, it can enter the critical code area.

For example:

public class www {
    
    
    public static void main(String[] args) {
    
    
        test test = new test();
        for (int i = 0; i < 10; i++) {
    
    
            new Thread(test).start();
        }
    }
}
class test  implements Runnable{
    
    
    int num = 0;
    private void add(){
    
    
        int temp;
        for (int i = 0; i < 1000; i++) {
    
    
            temp = num;
            temp++;
            num  = temp;
        }
        System.out.println(Thread.currentThread().getName() + "  "+ num);
    }
    public void run(){
    
    
        synchronized (this){
    
    
            add();
        }
    }
}

Insert image description here

In this way, the result of each thread's execution is an integer.

Modify the add() method with synchronized to get the same result. This is the synchronization method discussed below.

private synchronized void add(){
    
    
        int temp;
        for (int i = 0; i < 1000; i++) {
    
    
            temp = num;
            temp++;
            num  = temp;
        }
        System.out.println(Thread.currentThread().getName() + "  "+ num);
    }

sync method

Synchronized methods, like synchronized code blocks, use interlocking to achieve code synchronization access.

public class www {
    
    
    public static void main(String[] args) {
    
    
        test test = new test();
        for (int i = 0; i < 10; i++) {
    
    
            new Thread(test).start();
        }
    }
}
class test  implements Runnable{
    
    
    int num = 0;
    private synchronized void add(){
    
    
        int temp;
        for (int i = 0; i < 1000; i++) {
    
    
            temp = num;
            temp++;
            num  = temp;
        }
        System.out.println(Thread.currentThread().getName() + "  "+ num);
    }
    public void run(){
    
    
            add();
    }
}

Insert image description here

Synchronization is a high-cost operation, so try to avoid using synchronizesynchronization methods with large settings. Under normal circumstances, use synchronizecode blocks to synchronize key codes.

Thread communication

Sometimes multi-threads have order problems during execution. Java provides three methods to solve thread communication problems. wait()They are , notify(), methods respectively notifyAll(). These three keywords can only work within the scope of the synchronized keyword, and they have practical significance only when paired with these three methods in the same method.

wait()Method: The thread calling this method can release the lock of the shared resource and enter the waiting state from the runnable state. Know to be awakened again.

notify()Method: You can wake up the first thread in the waiting queue waiting for the same shared resource, and make the process exit the waiting state and enter the runnable state.

notifyAll()Method: All processes that are waiting for the same shared resource in the waiting queue can exit from the waiting state and enter the runnable state. If there are multiple processes, whichever one has the highest priority will run first. Use this method when you don't know which process to wake up.
Supplementary knowledge points:
Detailed explanation of the five states of threads

Here's an example:

public class www {
    
    
    public static void main(String[] args) {
    
    
        //实例化一个ShareStore对象 并创建进程启动
        ShareStore shareStore = new ShareStore();
        new Consumer(shareStore).start();
        new Producer(shareStore).start();
    }
}

/***
 * 生产者
 */
class Producer extends Thread{
    
    
    private ShareStore shareStore;
    Producer(ShareStore shareStore){
    
    
        this.shareStore = shareStore;
    }
    @Override
    public void run() {
    
    
        int num = 1;
        while (true){
    
    
            shareStore.setShareNum(++num);
            System.out.println("Producer生产了一个数字"+num);
            //睡眠1s
            try {
    
    
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
                throw new RuntimeException(e);
            }
        }
    }
}
/***
 * 消费者
 */
class Consumer extends Thread{
    
    
    private ShareStore shareStore;
    Consumer(ShareStore shareStore){
    
    
        this.shareStore = shareStore;
    }
    @Override
    public void run() {
    
    
        int num = 1;
        while (true){
    
    
            num = shareStore.getShareNum();
            System.out.println("Consumer消费了一个数字"+num);
            //睡眠1s
            try {
    
    
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
                throw new RuntimeException(e);
            }
        }
    }
}

/***
 * 用来管理的
 */
class ShareStore{
    
    
    private int num;
    private boolean writeable = true;
    public synchronized void setShareNum(int num){
    
    
        if (!writeable){
    
    
            try {
    
    
                wait();//等待消费者消费完成
            } catch (InterruptedException e) {
    
    
                throw new RuntimeException(e);
            }
        }
        this.num = num;
        writeable = false;
        notify(); //通知消费者 生产者已经生产可以消费
    }
    public synchronized int getShareNum(){
    
    
        if (writeable){
    
    
            try {
    
    
                wait(); //等待生产者生产出来
            } catch (InterruptedException e) {
    
    
                throw new RuntimeException(e);
            }
        }
        writeable = true;
        notify();//通知可以生产
        return this.num;
    }
}

Insert image description here

The output results are that the producer first produces a number, and the consumer then consumes it.

deadlock

Deadlock is a scenario: when two or more processes form a single-waiting ring, each process is waiting for each other's resource release, and neither of them can execute and continues to form a deadlock.

Guess you like

Origin blog.csdn.net/Systemmax20/article/details/125576353