java16-multithreading

program

  • In order to complete a specific task, a set of instructions written in a certain language, that is, a piece of static code

process

  • An execution of a program, or a program that is running
  • has its own birth, existence and death process

thread

  • Processes can be further refined into threads
  • If a process executes multiple threads at the same time, it supports multithreading
  • Threads are the unit of scheduling and execution, each thread has an independent running stack and program counter (PC)
  • A process shares the same memory unit/memory address space => they allocate objects from the same heap, can access the same variables and objects
  • Communication between threads is simple and efficient, but system resources shared by multiple threads may pose security risks

Parallel and concurrent

  • Parallelism: Multiple CPUs perform multiple tasks
  • Concurrency: One CPU executes multiple tasks at the same time

Thread

  • start() starts the current thread and calls run() in the current thread
  • run() usually needs to override this method in the Thread class, and declare the operations to be performed by the created thread in this method
  • currentThread() static method, returns the thread of the current code
  • getName() gets the name of the current thread
  • setName() sets the name of the current thread
  • yield() releases the execution right of the current CPU
  • join() calls join() of thread b in thread a. At this time, thread a enters the blocking state. Thread a does not end the blocking state until thread b is completely executed.
  • sleep() makes the current thread sleep for a period of time
  • isAlice() to see if the thread has ended
public class ThreadMethods {
    
    
    public static void main(String[] args) {
    
    
        ThreadMethod1 t1=new ThreadMethod1();
        t1.setName("线程一");
        t1.start();
        // 主线程命名
        Thread.currentThread().setName("主线程");
        for (int i = 0; i < 100; i++) {
    
    
            if(i%2==0){
    
    
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
            if(i==20){
    
    
                try {
    
    
                    t1.join();
                } catch (InterruptedException e) {
    
    
                    throw new RuntimeException(e);
                }
            }

        }
        System.out.println(t1.isAlive());
    }
}
class ThreadMethod1 extends Thread{
    
    
    @Override
    public void run() {
    
    
        for (int i = 0; i < 100; i++) {
    
    
            if(i%2==0){
    
    
                System.out.println(Thread.currentThread().getName()+":"+i);
            }

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

        }
    }
}

Multi-threaded creation

Inherited from the Thread class

  1. Create a subclass that inherits from Thread
  2. Override the run() method of the Thread class
  3. Create an object of a subclass of the Thread class
  4. Call start() on this object
  5. The run() method cannot be called directly
  6. It is not possible to let the thread that has already started() execute again
class MyThread extends Thread{
    
    
    @Override
    public void run() {
    
    
        for (int i = 0; i < 100; i++) {
    
    
            if(i%2==0){
    
    
                System.out.println(i);
            }

        }
    }
}
public class ThreadTest {
    
    
    public static void main(String[] args) {
    
    
        MyThread t1 = new MyThread();
        t1.start();
        for (int i = 0; i < 100; i++) {
    
    
            if(i%2==0){
    
    
                System.out.println(i+"****main****");
            }

        }
    }
}

Implement the Runnable interface

  1. Create a class that implements the Runnable interface
  2. Implement the class to implement the abstract method run() in Runnable
  3. Create an object of the implementation class
  4. Pass this object as a parameter to the constructor of the Thread class to create an object of the Thread class
  5. Call start() through the Thread class
public class ThreadRunnable {
    
    
    public static void main(String[] args) {
    
    
        Runnable1 r1=new Runnable1();
        Thread t1=new Thread(r1);
        t1.setName("线程1");
        t1.start();
        Thread t2=new Thread(r1);
        t2.setName("线程2");
        t2.start();
    }
}
class Runnable1 implements Runnable{
    
    
    @Override
    public void run() {
    
    
        for (int i = 0; i < 100; i++) {
    
    
            if(i%2==0){
    
    
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
}

Advantages of the Runnable interface

  1. The implementation does not have the limitations of single inheritance of classes
  2. The implementation is more suitable for dealing with the situation where multiple threads share data

Callable interface

  1. Implementation class that implements Callable
  2. Implement the call() method, and declare the method that this thread needs to execute in call()
  3. Create an object of the Callable interface implementation class
  4. Pass the object of the Callable interface implementation class as a parameter to the FutureTask object
  5. Pass the FutureTask object as a parameter to Thread, and call the start() method to start the thread
  6. Get Callable return value
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CallableTest {
    
    
    public static void main(String[] args) {
    
    
        Test t1=new Test();
        FutureTask futureTask = new FutureTask(t1);
        new Thread(futureTask).start();
        try {
    
    
            Object o1=futureTask.get();
            System.out.println(o1);
        } catch (InterruptedException e) {
    
    
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
    
    
            throw new RuntimeException(e);
        }
    }
}
class Test implements Callable {
    
    
    @Override
    public Object call() throws Exception {
    
    
        int sum=0;
        for (int i = 0; i < 100; i++) {
    
    
            if(i%2==0){
    
    
                System.out.println(i);
                sum+=i;
            }

        }
        return  sum;
    }
}

Advantages over the Runnable interface

  1. Can have a return value in the run() method
  2. can throw an exception
  3. Support for generic return values
  4. You need to borrow the FutureTask class, such as getting the returned result

Thread Pool

  1. Create multiple threads in advance, put them into the thread pool, obtain them directly when using them, and put them back into the pool after use
  2. It can avoid repeated creation and destruction and realize reuse
  3. Provide a specified number of thread pools
  4. To execute the specified thread operation, you need to provide an object that implements the Runnable or Callable interface implementation class
  5. Close the thread pool
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoll {
    
    
    public static void main(String[] args) {
    
    
        ExecutorService service = Executors.newFixedThreadPool(10);
        NumThread1 n1=new NumThread1();
        NumThread2 n2=new NumThread2();
        service.execute(n1); // 适合Runnable
        service.execute(n2);
        // service.submit(); // 适合Callable
        // 关闭
        service.shutdown();
    }
}
class NumThread1 implements Runnable{
    
    
    @Override
    public void run() {
    
    
        for (int i = 0; i < 100; i++) {
    
    
            if(i%2==0){
    
    
                System.out.println(Thread.currentThread().getName()+"名字为"+i);
            }
        }
    }
}
class NumThread2 implements Runnable{
    
    
    @Override
    public void run() {
    
    
        for (int i = 0; i < 100; i++) {
    
    
            if(i%2!=0){
    
    
                System.out.println(Thread.currentThread().getName()+"名字为"+i);
            }
        }
    }
}

thread priority

  • priority
    • MAX_PRIORITY:10
    • NORM_PRIORITY:5
    • MIN_PRIORITY:1
  • set up
    • getPriority() gets thread priority
    • setPriority(int p) sets thread priority
  • After setting the priority, it is not completely safe to prioritize. It can only be said that a thread with a higher priority has a higher probability of execution than a thread with a lower priority.

thread life cycle

  • New: When an object of the Thread class or its subclasses is declared and created, the new thread object is in the new state
  • Ready: After being started () in the new state, it will enter the thread queue and wait for the CPU time slice. At this time, it already has the conditions to run, but it has not been allocated CPU resources.
  • Running: When a ready thread is scheduled and obtains CPU resources, it enters the running state. run() defines the operation and function of the thread
  • Blocking: In a special case, when it is artificially suspended or performs input and output operations, it will give up the CPU and temporarily terminate its own execution, entering the blocked state
    • sleep(long time)sleep
    • join()
    • wait for synchronization lock
    • wait()
    • suspend()
  • Death: The thread has completed all its work or the thread is forcibly terminated early or terminated due to an exception
    • Execute run()
    • Call the thread's stop()
    • Error/Exception occurs and is not processed

thread safety issues

Synchronized code block

  1. The code that operates on shared data is the code that needs to be synchronized
  2. Shared data: Variables that multiple threads operate on together
  3. Synchronization monitor: commonly known as lock
    1. Objects of any class can be called locks
    2. The same thread uses the same lock
  4. synchronized (sync monitor) { share operation }
public class WindowTest2 {
    
    
    public static void main(String[] args) {
    
    
        Window2 w1=new Window2();
        Thread t1=new Thread(w1);
        Thread t2=new Thread(w1);
        Thread t3=new Thread(w1);
        t1.start();
        t2.start();
        t3.start();
    }
}
class Window2 implements Runnable{
    
    
    public int ticket=100;
    Object o1=new Object();
    @Override
    public void run() {
    
    
        while (true){
    
    
            synchronized (o1) {
    
    
                if (ticket > 0) {
    
    
                    try {
    
    
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
    
    
                        throw new RuntimeException(e);
                    }
                    System.out.println("买票,票号:" + ticket);
                    ticket--;
                } else {
    
    
                    break;
                }
            }
        }
    }
}

synchronization method

  1. If the code for manipulating shared data is completely declared in a method, we might as well make the method declaration synchronous
  2. For non-static synchronization methods, the synchronization monitor is: this; for static synchronization methods, the synchronization monitor is the current class itself
public class WindowTest3 {
    
    
    public static void main(String[] args) {
    
    
        Window3 w3=new Window3();
        Thread t1=new Thread(w3);
        Thread t2=new Thread(w3);
        Thread t3=new Thread(w3);
        t1.start();
        t2.start();
        t3.start();
    }
}
class Window3 implements Runnable{
    
    
    private int ticket=100;
    @Override
    public void run() {
    
    
        show();
    }
    public synchronized void show(){
    
    
        while (true){
    
    
            if(ticket>0){
    
    
                try {
    
    
                    Thread.sleep(100);
                } catch (InterruptedException e) {
    
    
                    throw new RuntimeException(e);
                }
                System.out.println("卖票中,票号:"+ticket);
                ticket--;
            }else {
    
    
                break;
            }
        }
    }
}

LOCK lock

  1. The java.util.concurrent.locks.Lock interface is a tool for controlling access to shared resources by multiple threads
  2. The lock provides exclusive access to shared resources. Only one thread can lock the LOCK object at a time. The thread should obtain the LOCK object before accessing the shared resource.
  3. LOCK, which has the same concurrency and memory semantics as synchronized, is more commonly used in implementing thread safety control, and can display locking and releasing locks
import java.util.concurrent.locks.ReentrantLock;

public class LockTest {
    
    
    public static void main(String[] args) {
    
    
        Window4 w3=new Window4();
        Thread t1=new Thread(w3);
        Thread t2=new Thread(w3);
        Thread t3=new Thread(w3);
        t1.start();
        t2.start();
        t3.start();
    }

}
class Window4 implements Runnable{
    
    
    public int ticket=100;
    // 实例化lock
    private ReentrantLock lock=new ReentrantLock();
    @Override
    public void run() {
    
    
        while (true){
    
    
            try{
    
    
                // 锁
                lock.lock();
                if(ticket>0){
    
    
                    try {
    
    
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
    
    
                        throw new RuntimeException(e);
                    }
                    System.out.println(Thread.currentThread().getName()+"卖票,票号:"+ticket);
                    ticket--;
                }else {
    
    
                    break;
                }
            }finally {
    
    
                // 解锁
                lock.unlock();
            }
        }
    }
}

thread deadlock problem

  1. Different threads occupy the synchronization resources needed by the other party and do not give up. They are all waiting for the other party to give up the synchronization resources they need, which forms a deadlock.
  2. No exception will be thrown after deadlock, and the threads of the program are all blocked and cannot continue

thread communication

public class CommunicationTest {
    
    
    public static void main(String[] args) {
    
    
        Number n1=new Number();
        Thread t1=new Thread(n1);
        Thread t2=new Thread(n1);
        t1.start();
        t2.start();
    }
}
class Number implements Runnable{
    
    
    public int num=1;
    @Override
    public synchronized void run() {
    
    
        while (true){
    
    
            // 唤醒线程,只唤醒一个 notofyAll()可以唤醒所有的
            notify();
            try {
    
    
                Thread.sleep(100);
            } catch (InterruptedException e) {
    
    
                throw new RuntimeException(e);
            }
            if(num<=100){
    
    
                System.out.println(Thread.currentThread().getName()+"打印"+num);
                num++;
            }else {
    
    
                break;
            }
            try {
    
    
                // 让当前线程进入阻塞状态,并释放同步监视器
                wait();
            } catch (InterruptedException e) {
    
    
                throw new RuntimeException(e);
            }
        }
    }
}

The difference between sleep() and wait()

Same point

  1. Once the method is executed, the current thread can be blocked

the difference

  1. The positions of the two method declarations are different: sleep() is declared in the Thread class, and wait() is declared in the Object class
  2. sleep() can be called in any scenario, wait() must be called in a synchronous code block or a synchronous method
  3. If both methods are in a synchronized code block or a synchronized method, sleep() will not release the lock, and wait() will release the lock

Guess you like

Origin blog.csdn.net/weixin_64925940/article/details/126909972