[Java Multithreading Explanation-Part 2] To understand multithreading, this article is enough

Foreword: In today's Internet industry, multi-threading technology has become a very important skill. With the development of computer hardware, more and more programmers are paying attention to multi-threading technology, hoping to improve program performance through multi-threading. As a widely used programming language, Java also provides rich multi-threading support. This article will introduce in detail the basic concepts, principles, implementation methods and applications of Java multi-threading in life to help readers better understand and master Java multi-threading technology.

Insert image description here

1.Thread attributes

1. Thread interruption
There are three ways to interrupt a thread

  • The thread ends and ends naturally
  • An uncaught exception occurred in the method and the thread was terminated.
  • Use the stop method (deprecated)
  • Use the interrupt method to set the interrupt status of the thread. To find out whether the interrupt status is set, first call the static Thread.currentThread method to get the current thread, and then call the isInterrupted method. However, if the thread is blocked, the interrupt status cannot be checked. This is where Interrupt Exception will be caused. When the interrupt method is called on a thread that is blocked by a sleep or wait call, that blocking call (that is, the sleep or wait call) will be interrupted by an InterruptedException.
class test implements Runnable {
    
    

    public void run() {
    
    
      try{
    
    
        while(true){
    
    
         System.out.println("在运行");
             Thread.sleep(1000);

      }catch(InterruptedException e){
    
    
        
          Thread.currentThread().interrupt();

      }
    }
}

2. Daemon thread

Convert a thread into a daemon thread (daemonthread). There is no magic in such a thread. The only purpose of a daemon thread is to provide services to other threads. The timer thread is an example. It regularly sends "timer tick" signals to other threads. In addition, the thread that clears expired cache items is also a daemon thread. When only the daemon thread is left, the virtual machine will exit. Because if only the daemon thread is left, there is no need to continue running the program. Consider the following example.

class Counter {
    
    
    //线程个数
    private int count;

    public synchronized int getCount() {
    
    
        return count;
    }
//增加线程个数
    public synchronized void incrementCount() {
    
    
        count++;
    }
//减少线程个数
    public synchronized void decrementCount() {
    
    
        count--;
    }
}

//显示当前线程个数
class Display implements Runnable {
    
    
    private Counter counter;

    public Display(Counter counter) {
    
    
        this.counter = counter;
    }

    @Override
    public void run() {
    
    
        while (true) {
    
    
            try {
    
    
                System.out.println("Number of threads: " + counter.getCount());
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
             break;
            }
        }
    }
}

public class Main {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        final Counter counter = new Counter();
        Thread displayThread = new Thread(new Display(counter));
        displayThread.start();
        for (int i = 0; i < 10; i++) {
    
    
            Thread.sleep(400);
            new Thread(new Runnable() {
    
    
                @Override
                public void run() {
    
    
                    //每创建一个线程就要更新counter
                    counter.incrementCount();
                    try {
    
    
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                    //每结速一个线程也要更新counter
                    counter.decrementCount();
                }
            }).start();
        }
        Thread.sleep(10000);
        System.out.println("main线程已退出");

    }
}

Insert image description here
It can be seen from this code that if the counter thread is not set as a daemon thread, then after the main thread ends, as long as the counter thread is running, the program will still be running. When the main thread ends, we no longer need the counter. At this time, the daemon thread comes in handy. Set the displayThread thread to a daemon thread through displayThread.setDaemon(true);.

2. Thread synchronization on mutual exclusion

In most practical multi-threaded applications, two or more threads need to share access to the same data. What happens if two threads access the same object, and each thread calls a method that modifies the state of the object? As you can imagine, the two threads will back each other up. Depending on the order in which threads access data, objects may be corrupted. This situation is often called a race condition.
In a multi-threaded environment, in order to ensure the consistency and integrity of data, synchronized and mutually exclusive access to shared resources is required. Java provides the synchronized keyword to achieve synchronization. Methods or code blocks modified by the synchronized keyword can only be accessed by one thread at the same time. In addition, Java also provides the Lock interface and its implementation classes (such as ReentrantLock) to implement mutually exclusive access.
Let’s look at an example first:

 class  test{
    
    

    public static void main(String[] args) {
    
    
        account account = new   account(5000);
        Thread a = new Thread(()->{
    
    
           while (account.getCash()>=500){
    
    

               try {
    
    
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
    
    
                   throw new RuntimeException(e);
               }
               account.drawMoney(500);
               System.out.println("余额:"+account.getCash());
           }
        });
        Thread b = new Thread(()->{
    
    
            while (account.getCash()>=1000){
    
    

                try {
    
    
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
    
    
                    throw new RuntimeException(e);
                }
                account.drawMoney(1000);
                System.out.println("余额:"+account.getCash());
            }
        });
        a.start();
        b.start();


    }
}
     class account{
    
    
    //账户余额
    private  int cash;
    public account(int cash){
    
    
        this.cash=cash;
    }
    //取钱
    public  void drawMoney(int money){
    
    
        this.cash-=money;
    }

    public int getCash() {
    
    
        return cash;
    }

    public void setCash(int cash) {
    
    
        this.cash = cash;
    }
}

Run result:
Insert image description here
In the above example, a and b withdraw money from the same account at the same time, because they can operate the account object at the same time, assuming that the balance is only 1,000 yuan at this time , both of them were very selfish and thought it would be over. In the end, both of them got the money, but the bank lost 500 yuan. Therefore, this kind of concurrent access to the same object, especially when modification operations are required, is very unsafe.
Solution 1:
Add synchronized annotation
Synchronized method: add it directly in front of the method

   public synchronized void drawMoney(int money){
    
    
        this.cash-=money;
    }

sync block

 Thread a = new Thread(()->{
    
    
          synchronized (account){
    
    
              while (true){
    
    
                 if (account.getCash() >= 1000) {
    
    

                    try {
    
    
                  
                        Thread.sleep(1000);
                        account.drawMoney(1000);
                    } catch (InterruptedException e) {
    
    
                        throw new RuntimeException(e);
                    } 
                

                    System.out.println("余额:" + account.getCash());
                }
                else break;
          }
        });

synchronized(object to be modified){ Code to be synchronized } No matter which method is used, synchronized is added to On the object or method to be modified


 Thread b = new Thread(() -> {
    
    

            while (true) {
    
    
                if (account.getCash() >= 500) {
    
    
                    try {
    
    
                    //获得锁
                        lock.lock();
                        Thread.sleep(1000);
                        account.drawMoney(500);
                    } catch (InterruptedException e) {
    
    
                        throw new RuntimeException(e);
                    } finally {
    
    
                    //释放锁
                        lock.unlock();
                    }
                    System.out.println("余额:" + account.getCash());

                }
              else break;
            }
        });

That’s it for this issue. If you think the writing is good, you can pay attention to the next issue.
Next issue preview:Insert image description here

Inter-thread communication
Use of thread pool
Application of Java multi-threading in life

Guess you like

Origin blog.csdn.net/weixin_55939638/article/details/134579110