Application of threads and multithreading in Java se

thread

Single thread execution path

1, Program, process, thread, CPU

Program: a set of instructions written in a certain language to complete a specific task. It refers to a piece of static code. The static code is stored in the hard disk. When the program is executed, the static code becomes a process.

Process: the program being executed. From the perspective of Windows, a process is the smallest unit of resource allocation by the operating system. A process contains at least one thread (main thread) and can contain multiple threads

Thread: a process can be further subdivided into threads. It is the smallest execution unit within a process and the smallest unit for task scheduling by the operating system. It belongs to a process. A thread can only depend on one process

CPU: a time node can only execute one task (thread)

2: Relationship between thread and process and CPU

1. A process contains at least one thread (main thread) and can contain multiple threads. A thread can only belong to one process, and threads cannot run independently of the process

2. Each process contains at least one thread (called the main thread); the program starts executing in the main thread, and the java program entry main () method is executed in the main thread

3. Other threads can be created and started in the main thread

4. All threads in a process share the memory resources of the process

5. The CPU executes in the unit of threads (if it is a single core CPU, a time node can only process one task. All threads in memory are executed in turns, and you feel that they are executed at the same time because the running speed of the CPU is too fast, so you feel that they are executed at the same time (if it is a multi-core CPU, a time node can execute multiple tasks))

3, There are two ways to create threads

1. Inherit Thread

The simplest way to implement threads in Java is to extend the Thread class and rewrite the run method. The method prototype is as follows:

The run party in the Thread class itself does not perform any operation. If we override the run method, it will execute the run method when the Thread starts.

Specific code

Mythread inherits the Thread class

public class Mysthread  extends Thread{
    @Override
    public void run() {
        for (int i = 0; i <1000 ; i++) {
            System.out.println("MyThread:"+i);
        }
    }
}

Program start

public class Main {
    public static void main(String[] args) {
        Mysthread mysthread=new Mysthread();
        mysthread.start();

        for (int i = 0; i <1000 ; i++) {
            System.out.println("Main"+i);
        }
    }
}

Note: the MyThread class object inheriting Thread is created here. Instead of the rewritten run() method, the start() method is directly called. If the run() method is called, it is either multithreaded or single threaded

2. Implement Runnable interface

There is only one abstract method public void run() in the java.lang.Runnable interface

To implement the thread by implementing the Runnable interface, you only need to implement the run method

The existence of Runnable interface is mainly to solve the problem that multiple inheritance is not allowed in Java

Specific code

Implement Runnable interface

public class Mysthread  implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i <1000 ; i++) {
            System.out.println("MyThread:"+i);
        }
    }
}

Start program

public class Main {
    public static void main(String[] args) {
        Mysthread mysthread=new Mysthread();
        Thread t=new Thread(mysthread);
        t.start();

        for (int i = 0; i <1000 ; i++) {
            System.out.println("Main"+i);
        }
    }

3. Connection and difference between the two methods

difference

(1) Inherit Thread: Thread code is stored in the run method of Thread subclass.

(2) Implement Runnable: the run method of the subclass of the interface in the thread code.

Benefits of realizing Runnable (I still don't understand it in the afternoon)

(1) The limitation of single inheritance is avoided

(2) Multiple threads can share the object of the same interface implementation class, which is very suitable for multiple same threads to process the same resource

Benefit specific code

public class Main2 {
    public static void main(String[] args) {
        Mysthread mysthread = new Mysthread();
        Thread t1 = new Thread(mysthread, "Thread one");  //Two threads share a Runnable object
        Thread t2 = new Thread(mysthread, "Thread two");

        t1.start();
        t2.start();
        Thread.currentThread().setPriority(1);    //You can also set the priority of the main thread
        for (int i = 0; i < 1000; i++) {
            System.out.println("Main Thread:" + i);
        }


    }
}

4, Thread method

1. Construction method

And the creation of two threads

2. Thread priority

(1)

  • In fact, the computer has only one CPU, and each thread obtains the right to use the CPU in turn to execute the task;
  • Threads with higher priority have more opportunities to obtain CPU, and vice versa;
  • The priority is expressed as an integer. The value range is 1 ~ 10. Generally, the default priority of threads is 5, but the priority can also be set or returned through setPriority and getPriority methods;

(2) Scheduling policy (CPU execution thread)

1) Time slice

The thread is given unit time to execute. According to the different tasks, the time slice allocated by the calculation of the operating system algorithm is also different

2) Preemptive

High priority threads have more execution rights, but it does not mean that low priority threads have no chance to execute

3) Three priorities

The Thread class has the following three static constants to represent the priority

MAX_PRIORITY: the value is 10, indicating the highest priority.

MIN_PRIORITY: the value is 1, indicating the lowest priority.

NORM_PRIORITY: the value is 5, indicating the default priority.

4) How to set and view priorities

Final void setpriority (int newpriority)) sets the priority of the thread
final int getpriority0 returns the priority of the thread

5) Specific code

public class Main2 {
    public static void main(String[] args) {
        Mysthread mysthread=new Mysthread();
        Thread t1=new Thread(mysthread,"Thread one");
        Thread t2=new Thread(mysthread,"Thread two");
        t1.setPriority(10);
        t2.setPriority(1);
        t1.start();
        t2.start();
        Thread.currentThread().setPriority(1);    //The main thread can also set priority
        for (int i = 0; i <1000 ; i++) {
            System.out.println("Main Thread:"+i);
        }
        System.out.println("======Priority of main thread:"+ Thread.currentThread().getPriority());
        System.out.println("======Priority of thread 1:"+t1.getPriority());
        System.out.println("======Priority of line 2:"+t2.getPriority());


    }
}

3. Common methods

Method application code

Sleep (long millis) lets the currently executing thread sleep (pause execution). The sleep time can be specified

public class Mysthread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            try {
                Thread.sleep(30);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}

join() waits for the thread to terminate

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Mysthread mysthread = new Mysthread();
        Thread t = new Thread(mysthread);
        Thread t1 = new Thread(mysthread);
        t.start();
        t.join();
        t1.start();
        for (int i = 0; i < 100; i++) {
            System.out.println("Main" + i);
        }
    }
}

final void setpriority() sets the priority of the thread
final int getpriority() returns the priority of the thread

public class Main2 {
    public static void main(String[] args) {
        Mysthread mysthread=new Mysthread();
        Thread t1=new Thread(mysthread,"Thread one");
        Thread t2=new Thread(mysthread,"Thread two");
        t1.setPriority(10);
        t2.setPriority(1);
        t1.start();
        t2.start();
        Thread.currentThread().setPriority(1);    //The main thread can also set priority
        for (int i = 0; i <1000 ; i++) {
            System.out.println("Main Thread:"+i);
        }
        System.out.println("======Priority of main thread:"+ Thread.currentThread().getPriority());
        System.out.println("======Priority of thread 1:"+t1.getPriority());
        System.out.println("======Priority of line 2:"+t2.getPriority());


    }
}

5, Thread state

A thread will be in different states during its life cycle:

New: when the object of a Thread class or its subclass is declared and created, the new Thread object is in the new state

**Ready: * * after the thread in the newly created state is start ed (), it will enter the thread queue and wait for the CPU time slice. At this time, it is ready to run, but it is not allocated CPU resources

**Run: * * when the ready thread is scheduled and obtains CPU resources, it enters the running state. The run () method defines the operation and function of the thread

**Blocking: * * in a special case, when the input / output operation is suspended or executed artificially, give up the CPU and temporarily suspend its execution to enter the blocking state

**Death: * * the thread has completed all its work, or the thread is forcibly aborted in advance or terminated due to an exception

6, Classification of threads

User thread and daemon thread

For example, any daemon thread is the nanny of all non daemon threads in the whole JVM: as long as any non daemon thread in the current JVM instance does not end, all daemon threads work; Only when the last non daemon thread ends, the daemon thread ends working with the JVM.

The role of daemon thread is to provide convenient services for the operation of other threads. The most typical application of daemon thread is GC (garbage collector), which is a very competent guardian.

There is almost no difference between user threads and daemon threads. The only difference is the departure of the virtual machine: if all user threads have exited and only daemon threads exist, the virtual machine will exit. Because there is no guardian, the guardian thread has no work to do, and there is no need to continue running the program

Note: setting the thread as a daemon thread must be before starting the thread, otherwise an IllegalThreadStateException will run.

Specific code:

User thread

/*
 User thread
 */
public class ThreadDemo1 extends  Thread{

    Socket socket;

    @Override
    public void run() {

        for (int i = 0; i < 100; i++) {
            /*try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }*/
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}

Daemon thread

/*
  Daemon thread
 */
public class ThreadDemo2 extends  Thread{


    @Override
    public void run() {
         while(true){
             /*try {
                 Thread.sleep(100);
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }*/
             System.out.println("I'm a daemon,I'm silently watching you, my guardian");
         }
    }

}

Run program

public class Test {

    public static void main(String[] args) {
        ThreadDemo1 userThread = new ThreadDemo1();
                    userThread.start();

        ThreadDemo2 sh = new ThreadDemo2();
                      sh.setDaemon(true);//Set thread 2 as a daemon thread, which must be set before startup
                      sh.start();
    }
}

7, Several thread cases

1. Realize interactive chat between two people

Server side

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(9966);
        Socket socket = serverSocket.accept();  //monitor
        ReceptionThread.socket = socket;
        SendThread.socket = socket;
        System.out.println("Start chatting");
        ReceptionThread receptionThread=new ReceptionThread();
        receptionThread.setName("service:");   //Set name
        SendThread sendThread=new SendThread();
        sendThread.setName("service:");    //Set name
        receptionThread.start();
        sendThread.start();

    }
}

Write thread

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Date;
import java.util.Scanner;

public class SendThread extends Thread{
  static Socket socket;
    @Override
    public void run() {
      Scanner scanner=new Scanner(System.in);
      System.out.println("Start chatting");
      while (true){
        try {
          String s=scanner.nextLine();
          OutputStream out=socket.getOutputStream();
          byte[] bytes=new byte[100];
          Date date=new Date();

          bytes=(Thread.currentThread().getName()+s+date.toLocaleString()).getBytes();
          int lenght=bytes.length;
          out.write(bytes,0,lenght);

        } catch (IOException e) {
          e.printStackTrace();
        }
      }

    }
}

read thread

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class ReceptionThread extends Thread {
    static Socket socket;
    @Override

    public void run() {
      while (true){
          try {
              InputStream in=socket.getInputStream();
              byte[] bytes=new byte[1024];
              int lenght=in.read(bytes);
              String s=new String(bytes,0,lenght);
              System.err.println(s);
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
    }
}

client

import java.io.IOException;
import java.net.Socket;

public class Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 9966);
        ReceptionThread.socket = socket;
        SendThread.socket = socket;
        System.out.println("Start chatting");
        ReceptionThread receptionThread=new ReceptionThread();
        receptionThread.setName("customer:");
        SendThread sendThread=new SendThread();
        sendThread.setName("customer:");
        receptionThread.start();
        sendThread.start();
    }
}

result

8, Multithreading

1. The concept of multithreading

Multithreading means that a program contains multiple execution units, that is, multiple different threads can be run simultaneously to perform different tasks in a program, that is, a single program is allowed to create multiple threads executing in parallel to complete their respective tasks.

When do I need multithreading

The program needs to perform two or more tasks at the same time.

When the program needs to implement some tasks that need to wait, such as user input, file read-write operation, network operation, search, etc.

When you need some programs running in the background

Advantages and disadvantages of multithreading

(1) Advantages

  • The program needs to perform two or more tasks at the same time.
  • When the program needs to implement some tasks that need to wait, such as user input, file read-write operation, network operation, search, etc.
  • When you need some programs running in the background

(2) Shortcomings

  • Threads are also programs, so threads need to occupy memory. The more threads, the more memory they occupy;
  • Multithreading needs coordination and management, so CPU time is required to track threads;
  • Threads' access to shared resources will affect each other, and the problem of competing for shared resources must be solved; (there is no such problem with multi-core and single core)

2. Thread synchronization

(1) Concurrency and parallelism

Concurrency: multiple CPU s perform multiple tasks. Multiple threads handle multiple tasks at the same time

Parallel: a single CPU executes multiple tasks at the same time (macroscopically) (in a single CPU system, there can only be one program execution (time slice) at each time, so these tasks can only be carried out alternately in time-sharing at the micro level. Because the speed of the CPU is too fast, you feel that they are carried out at the same time)

(2) Possible problems of multithreading (security problems) [important]

Multi core CPU: multiple threads will access a resource at the same time, resulting in security problems (for example, thread 1 comes in, but thread 2 also comes in before executing the code that can distinguish, which will cause problems. If two threads access the same resource, there will be problems)

Two solutions: [important]
(1) synchronized thread synchronization lock (synchronization monitor)

Add a thread synchronization lock (synchronization monitor) to ensure that only one thread accesses the shared resource at a time point. Add a lock to the shared resource. Which thread has the right to access the shared resource only after obtaining the lock

Synchronization monitor

The synchronization monitor can be any object and must be unique to ensure that multiple threads obtain the same object

(lock)

Synchronization monitor execution

1. The first thread accesses, locks the synchronization monitor and executes the code therein

2. When the second thread accesses, it is found that the synchronization monitor is locked and cannot be accessed

3. After the first thread is accessed, unlock the synchronization monitor

4. The second thread accesses, finds that the synchronization monitor has no lock, and then locks and accesses

There are two ways to create and add corresponding threads

Thread mode:

The first way to add synchronized

public class ThreadDemo extends Thread {
    static int num = 10;

    static Object object = new Object();

    @Override
    public void run() {

            while (true) {
                synchronized (object) {
                if (num > 0) {
                    try {
                        Thread.sleep(1000);
                        System.out.println(Thread.currentThread().getName() + "Selling the third" + num + "Ticket");
                        num--;
                    } catch (InterruptedException e) {
                        e.printStackTrace();

                    }
                } else {
                    System.out.println("The tickets have been sold out");
                    break;
                }
            }
        }


    }


}

The second way to add synchronized

public class ThreadDeom2 extends Thread {
    static int num = 10;

    @Override
    public void run() {
        while (true) {
            if (num > 0) {
                print();
            } else {
                break;
            }
        }
    }

    public static synchronized void print() {
        if (num > 0) {
            System.out.println(Thread.currentThread().getName() + "Selling the third" + num + "Ticket");
        }
    }
}

Start program

public class Test {
    public static void main(String[] args) {
        ThreadDemo threadDemo=new ThreadDemo();
        threadDemo.setName("Window one");
        ThreadDemo threadDemo1=new ThreadDemo();
        threadDemo1.setName("Window II");
        threadDemo.start();
        threadDemo1.start();
    }
}

Note: the first way to add synchronized () is to ensure that the same object is passed in (), so static modification is added before the created reference variable to ensure that the created reference object is loaded only once, and ensure that (different threads (all objects of the same Class)) pass in the same object every time in synchronized (). The second way to add synchronized is to modify the method with static. In fact, it is also to ensure that it is the same object, but here it is a Class object. Why is it the same object? Because it is the same object, multiple threads are synchronized with the method code in the same object (only when a thread accesses the method code in this object can the method or code in this object be locked to prevent other threads from accessing. If it is a different object, synchronized is meaningless.)

Runnable mode:

The first way to add synchronized

public class ThreadDemo implements Runnable {
    int num = 10;

    @Override
    public void run() {
//        The first way
        while (true) {
            synchronized (this) {
                try {
                    Thread.sleep(1000);
                    if (num > 0) {
                        System.out.println(Thread.currentThread().getName() + "Selling the third" + num + "Ticket");
                        num--;
                    } else {
                        System.out.println("The tickets have been sold out");
                        break;
                    }

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

this here is the currently running object

The second way to add synchronized

public class ThreadDemo implements Runnable {
    int num = 10;

    @Override
    public void run() {

        while (true){


            if (num>0){
                print();
            }else{
                System.out.println("The tickets have been sold out");
                break;
            }
        }
    }

    public synchronized void print() {
        if (num > 0) {

            System.out.println(Thread.currentThread().getName() + "Selling the third" + num + "Ticket");
            num--;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Note: because Runnable and Thread create threads in different ways, the usage of synchronized is also different, but one thing is the same, that is, synchronized must correspond to the same object, whether it is a common object or a Class object

(2) Lock

Lock

Starting with JDK 5.0, Java has provided a more powerful thread synchronization mechanism - synchronization is achieved by explicitly defining the synchronization Lock object. The synchronization Lock uses the Lock object as the.

The java.util.concurrent.locks.Lock interface is a tool that controls multiple threads to access shared resources. Locks provide exclusive access to shared resources. Only one thread can Lock the Lock object at a time. Threads should obtain the Lock object before accessing shared resources.

ReentrantLock class implements Lock. It has the same concurrency and memory semantics as synchronized. ReentrantLock is commonly used in thread safety control, which can explicitly add and release locks

Note: Lock is added at both ends of the code block

Specific code of a ticket buying case

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TicketThread  implements  Runnable{

       int num = 10;//Shared variable
       Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while(true){
                if(num>0){
                    print();
                }else{
                    break;
                }
        }
    }


    public void print(){
        //System.out.println("ssss");
        try {
            lock.lock();    //Lock
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(num>0){
                System.out.println(Thread.currentThread().getName()+":"+num);
                num--;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();//Release the lock in finally
        }
        //System.out.println("ssssssssssss");
    }


}



public class Test {


    public static void main(String[] args) {
        //Create ticket issue task
        TicketThread ticketThread = new TicketThread();

        //Two threads execute one task, num is only one
        Thread t1 = new Thread(ticketThread,"Window 1");
        Thread t2 = new Thread(ticketThread,"Window 2");

              t1.start();
              t2.start();

    }

}

3. Thread deadlock

deadlock

Different threads occupy the synchronization resources needed by the other party and do not give up. They are waiting for the other party to give up the synchronization resources they need, forming a thread deadlock

After deadlock occurs, no exception or prompt will appear, but all threads are blocked and cannot continue

Case: Chinese and Americans eat

Normal conditions:

Chinese: two chopsticks

American: knife and fork

exceptional case:

Chinese: a chopstick and a knife

American: a chopstick and a fork

The order of locks shall be clearly considered in the design to minimize the number of nested locking interactions.

Specific case code

Thread class where deadlock may occur

public class Dlock extends Thread {
    static Object objA = new Object();
    static Object objB = new Object();

    boolean flag;

    public Dlock(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        if (flag) {
            synchronized (objA) {
                System.out.println("ifobjA");
                synchronized (objB) {
                    System.out.println("ifobjB");
                }
            }
        } else {
            synchronized (objB) {
                System.out.println("elseobjB");
                synchronized (objA) {
                    System.out.println("elseobjB");
                }
            }
        }
    }
}

Create a multithread and start the program

public class Test {
    public static void main(String[] args) {
        Dlock dlock=new Dlock(true);
        Dlock dlock1=new Dlock(false);
        dlock.start();
        dlock1.start();
    }
}

Deadlock result

4. Thread communication

**Thread communication refers to the interaction between threads through message passing to achieve mutual containment and mutual scheduling.** 

Three methods are involved:

  • Once wait () executes this method, the current thread enters a blocking state and releases the synchronization monitor.
  • Once notify () executes this method, it wakes up a thread that is waiting. If multiple threads are waiting, wake up the one with higher priority.
  • Once notifyAll() executes this method, it wakes up all the wait ing threads.

explain:

  • The three methods wait(), notify(), notifyAll() must be used in the synchronization code block or synchronization method.
  • The three methods wait(), notify(), notifyAll() are defined in the java.lang.Object class

Note: wait(), notify() and synchronized are used together

Threaded communication plus multithreading case

Case:

Classic example: Producer / consumer problem

The producer puts the product at the counter, while the customer takes the product from the counter. The producer can only produce a fixed number of products at a time (for example: 1). At this time, the product can no longer be placed in the counter. At this time, the producer should stop production and wait for the consumer to take the product. At this time, the producer wakes up the consumer to take the product. After the consumer takes the product, wake up the producer and the consumer begins to wait

code

Counter class

public class Counter {
    int num=0;


    public synchronized void production(){  //Production function
        if (num==0){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            num++;
            System.out.println("Producer production");
            notify();
        }else{
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    public synchronized void consumption(){  //Consumption function
        if (num>0){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            num--;
            System.out.println("Consumer consumption");
            notify();
        }else{
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Producer thread:

public class ProtuctionThread extends Thread {
    Counter counter;

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

    @Override
    public void run() {
        while (true) {
            counter.production();
        }

    }
}

Consumer thread

public class ConsumptionThread  extends Thread{
    Counter counter;

    public ConsumptionThread(Counter counter){
        this.counter=counter;
    }
    @Override
    public void run() {
        while (true)
            counter.consumption();
    }
}

Test (start) procedure

public class Test {
    public static void main(String[] args) {

        Counter counter=new Counter();
        ConsumptionThread consumptionThread=new ConsumptionThread(counter);
        ProtuctionThread protuctionThread=new ProtuctionThread(counter);
        consumptionThread.start();
        protuctionThread.start();

    }
}

result

8, New thread creation mode

Implement Callable interface

Compared with Runnable, Callable is more powerful

  • Compared with the run () method, you can have a return value

  • Method can throw an exception

  • Return values that support generics

  • You need to get the returned result and receive the task with the help of FutureTask class

code

Implementation class under Callable interface

import java.util.concurrent.Callable;

public class CallableThread implements Callable {
    @Override
    public Integer call() throws Exception {
        int sum=0;
        for (int i = 0; i <10 ; i++) {
            sum+=i;
        }

        return sum;
    }
}

Create launch application

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CallableThread callableThread=new CallableThread();
        FutureTask futureTask=new FutureTask(callableThread);
        Thread t=new Thread(futureTask);
        t.start();

        Integer a= (Integer) futureTask.get();
        System.out.println(a);
    }
}

Note: the return value of the overridden call () method must be a reference type

Keywords: Java Windows Multithreading JavaSE

Added by rline101 on Fri, 08 Oct 2021 07:45:45 +0300