Java Foundation Multithreading & JUC Comprehensive Study Notes

Getting to know multithreading

What is multithreading?
Thread
Thread is the smallest unit that the operating system can perform operation scheduling.Threads are contained in processes, is the actual operating unit in the process .

Simple understanding: functions that are independent of each other and can run simultaneously in the application software

So what is a process?
Process
A process is the basic execution entity of a program. For example, if you open the task manager, you can see that there are many processes in it.

多线程作用:提高效率

Two concepts of multithreading

Concurrency
Concurrency: At the same time, multiple instructions are executed alternately on a single CPU

Parallel
Parallel: At the same time, multiple instructions are executed simultaneously on multiple CPUs


Implementation of multithreading

① Implementation by inheriting the Thread class

The first way to start multithreading:

1. Define a class by yourself to inherit Thread
2.Rewrite the run method
3. Create an object of the subclass and start the thread

	public class MyThread extends Thread{
    
    
		@Override
		public void run() {
    
    
		//书写线程要执行代码
			for (int i = e; i < 100; i++) {
    
    
				system.out.println(getName( ) + "Helloworld" );
			}
		}
	}

		public static void main( String[] args) {
    
    
			MyThread t1 = new MyThread();
			MyThread t2 = new MyThread() ;
			t1.setName("线程1");
			t2.setName("线程2");
			t1.start();
			t2.start();
		}


② Realize by implementing the Runnable interface

The second way to start multi-threading:
1. Define a class by yourself to implement the Runnable interface 2. Rewrite the run method inside
3. Create an object of your own class
4. Create an object of the Thread class and start the thread

    public static void main(String[] args) {
    
    
        //创建实现Runnable接口的类的对象
        RunnableInf run = new RunnableInf();
        //创建线程对象
        Thread t1 = new Thread(run);
        Thread t2 = new Thread(run);
        //给线程设置名字
        t1.setName("线程一");
        t2.setName("线程二");
        //开启线程
        t1.start();
        t2.start();

    }
public class RunnableInf implements Runnable{
    
    
    @Override
    public void run() {
    
    
        //书写线程执行代码
        for (int i = 0; i < 100; i++){
    
    
            System.out.println(Thread.currentThread().getName()+"hello world");
        }
    }
}


③ Realize by using Callable interface and Future interface

The third implementation of multithreading:

Features: Can get multi-threaded runningresult

The steps are as follows
1. Create a class MyCallable to implement the callable interface
2. Rewrite call (it has a return value, indicating the result of multi-threaded operation)
3. Create an object of MyCallable (representing the tasks to be performed by multithreading)
4. Create the object of FutureTask (function to manage the result of multi-threaded operation)
5.Create an object of the Thread class and start it (representing a thread)

    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        //创建Mycallable的对象(表示多线程要执行的任务)
        MyCallable mc = new MyCallable();
        //创建FutureTask的对象(作用管理多线程运行的结果)
        FutureTask<Integer> ft = new FutureTask<>(mc ) ;
        //创建线程的对象
        Thread t1 = new Thread(ft);
        //启动线程
        t1.start();

        //获取线程运行结果
        Integer result = ft.get();
        System.out.println(result);
    }
public class MyCallable implements Callable<Integer> {
    
    
    @Override
    public Integer call() throws Exception {
    
    
        //求100之间的和
        int sum = 0;
        for (int i = 0; i <= 100; i++ ){
    
    
            sum += i;
        }
        return sum;
    }
}


Comparison of three implementations

insert image description here


common member methods

insert image description here

method details

void setName(string name)
sets the name of the thread (the constructor can also set the name)
Details :
1. If we do not set a name for the thread, the thread also has a default name
format: Thread-X (X serial number, starting from 0 )
2. If we want to set the name for the thread, we can use the set method to set it, or we can set it by the construction method (remember to inherit the construction method of the Thread class, the shortcut key Alt+Ins)


static Thread currentThread( )
Obtain the object
details of the current thread :
when the JVM virtual machine starts, it will automatically start multiple threads, one of which is called the main thread. Its
function is to call the main method and execute the code inside. In the past, all the code we wrote was actually running in the main thread


static void sleep(long time)
let the thread sleep for the specified time, the unit is milliseconds
Details :
1. Which thread executes this method, then which thread will stay here for the corresponding time
2. The parameter of the method: it means sleep The time, in milliseconds; 1 second = 1000 milliseconds
3. When the time is up, the thread will automatically wake up and continue to execute other codes below


Two ways of thread scheduling

  • The biggest feature of preemptive scheduling is randomness, according topriorityTo seize cpu resources, the higher the priority, the greater the probability of preemption
  • Non-preemptive scheduling features: once for you, once for me, regular
    insert image description here

The priority is from 0 to 10, and the default priority is 5, as
you can see from the Thread source code

insert image description here


final void setDaemon(boolean on)
is set to the details of the daemon thread:
when other non-daemon threads are executed, the daemon threads will end one after another, not immediately

Easy to understand: According to the case in the picture below,
when the goddess thread is over, there is no need for a spare tire

goddess thread
insert image description here
spare tire thread
insert image description here

in the main method
insert image description here

The final result is that when thread 1 loops 10 times, the daemon threads end one after another (the loop will not end, nor will it stop immediately)


Daemon thread usage scenarios
such as QQ chat, file transfer
insert image description here


Letting Threads Explain

insert image description here


Insert thread demo

    public static void main(String[] args) throws InterruptedException {
    
    
        //创建实现Runnable接口的类的对象
        RunnableInf run = new RunnableInf();
        //创建线程对象
        Thread t1 = new Thread(run);
        //Thread t2 = new Thread(run);
        //给线程设置名字
        t1.setName("线程一");    
        //开启线程
        t1.start();        
        //把t1线程插入到当前线程之前
        t1.join();  //这个代码运行在哪个线程上,就插入在哪个线程前

        for (int i = 0; i < 10; i++){
    
    
            System.out.println("main线程"+i);
        }
    }

thread life cycle

insert image description here


Thread Safety Issues

Since threads seize CPU resources randomly , when performing business operations, multiple threads may execute the same business in parallel, resulting in data confusion or loss; for example, in the classic case of buying tickets, it is possible that one thread enters and executes buying tickets Logically, another thread also entered, resulting in the sale of two tickets with the same number. The classic problem is overselling, more than one ticket is sold, and there are tickets that are not sold.

The solution is naturally to add a lock to the executed business code to ensure atomicity


Method One: Synchronized Code Blocks

Lock down code that manipulates shared data

insert image description here

特点1:锁默认打卉,有一个线程进去了,锁自动关闭
特点2:里面的代码全部执行完毕,线程出来,锁自动打开

The synchronous code block is used as follows, the case of selling 100 tickets

public class MyThread extends Thread{
    
    

    //表示这个类的所有对象共享ticket数据
    static int ticket = 0;

    //创建锁对象,一定要唯一
    static Object object = new Object();

    @Override
    public void run() {
    
    
        while (true){
    
    
            //同步代码块
            synchronized (object){
    
    
                if (ticket < 100){
    
    
                    ticket++;
                    System.out.println(getName()+"正在卖第" +ticket +"张票");
                }else {
    
    
                    break;
                }
            }
        }
    }
}

public class Treaddemo {
    
    
    public static void main(String[] args) {
    
    
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        t1.setName("窗口1");
        t2.setName("窗口2");

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

Method 2: The synchronization method
insert image description here
is to add the synchronized keyword to the method

Feature 1: The synchronization method is to lock all the code in the method

Feature 2: The lock object cannot be specified by itself
Non-static: this
Static: The bytecode file object of the current class


The case of selling 100 tickets

public class MyRunnable implements Runnable{
    
    

    int ticket = 0;//由于MyRunnalbe方法只new了一个对象,可以不用把ticket设置为共享变量

    @Override
    public void run() {
    
    
        while (true){
    
    
            if (method()) break;
        }
    }

    private synchronized boolean method() {
    
    
        synchronized (MyRunnable.class){
    
    
            if ( ticket == 100 ){
    
    
                return true;
            }else {
    
    
                try {
    
    
                    Thread.sleep(100);
                } catch (InterruptedException e) {
    
    
                    throw new RuntimeException(e);
                }
                ticket++;
                System.out.println(Thread.currentThread().getName()+ "正在卖第"+ ticket +"张票");
            }
        }
        return false;
    }
}
public class Threaddemo {
    
    
    public static void main(String[] args) {
    
    
        MyRunnable mr = new MyRunnable();

        Thread t1 = new Thread(mr);
        Thread t2 = new Thread(mr);
        Thread t3 = new Thread(mr);

        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");

        t1.start();
        t2.start();
        t3.start();
    }
}


Method Three: Synchronous Method

In order to express more clearly how to lock and release the lock, JDK5 provides a new lock object Lock, which can be manually locked and released manually.

The Lock implementation provides a wider range of locking operations
than using synchronized methods and statements . Lock provides methods for acquiring and releasing locks.

void lock():获得锁
void unlock():释放锁

Lock is an interface that cannot be instantiated directly, and its implementation class ReentrantLock is usually used to instantiate the construction method of ReentrantLock

ReentrantLock(): Create an instance of ReentrantLock

Still taking the example of buying 100 tickets, the code for using the lock is as follows

public class LockThread extends Thread{
    
    
    static int ticket = 0;
    static Lock lock = new ReentrantLock();

    public LockThread() {
    
    
    }

    public LockThread(String name) {
    
    
        super(name);
    }

    @Override
    public void run() {
    
    
        while (true){
    
    
            //这次换用锁的方式
            lock.lock();//上锁
            try {
    
    
                if (ticket < 100){
    
    
                    Thread.sleep(100);
                    ticket++;
                    System.out.println(getName()+"正在卖第" +ticket +"张票");
                }else {
    
    
                    break;
                }
            } catch (InterruptedException e) {
    
    
                throw new RuntimeException(e);
            } finally {
    
    
                lock.unlock();
            }
        }
    }
}

public class MainThread {
    
    
    public static void main(String[] args) {
    
    
        LockThread t1 = new LockThread("窗口一");
        LockThread t2 = new LockThread("窗口二");
        LockThread t3 = new LockThread("窗口三");

        t1.start();
        t2.start();
        t3.start();
    }
}

deadlock

In layman's terms, locks are nested. Thread 1 takes A lock. When thread 1 takes A lock, thread 2 enters B lock, and the operation in the middle of thread 1 needs to enter B lock again to execute the business and end; And the intermediate operation of thread 2 needs to re-enter the A lock to execute the business and end. Eventually both are waiting for the other to release the lock, resulting in a deadlock situation


producer and consumer

Waiting for wake-up mechanism
The waiting for wake-up mechanism will allow two threads to execute in turn, the standard one time for you and one time for me

insert image description here

Details: notify() wakes up a thread at random, which is not easy to control. Generally, the notifyAll() method is used

The following is a classic consumer and producer case code, the consumer is the eater, the producer is the chef, the middle control is the table, and the control thread executes

middle controller table

public class Desk {
    
    
    /**
     * 控制消费者和生产者的执行
     */

    //是否有面条 0:没有面条  1:有面条
    public static int foodFlag = 0;

    //总个数,也就是消费者需要的总个数
    public static int count = 10;

    //锁对象
    public static Object lock = new Object();
}

diners

public class Eater extends Thread{
    
    
    /**
     * 1.循环
     * 2.同步代码块
     * 3.判断共享数据是否到了末尾(到了末尾执行的逻辑)
     * 4.判断共享数据是否到了末尾(没到末尾执行的逻辑)
     */
    @Override
    public void run() {
    
    
        while (true){
    
    
            if (Desk.count == 0){
    
    
                break;
            }
            synchronized (Desk.lock){
    
    
                if (Desk.foodFlag == 0){
    
    //先看是否有产品,没有就等待
                    try {
    
    
                        Desk.lock.wait();//让当前线程跟锁绑定
                    } catch (InterruptedException e) {
    
    
                        throw new RuntimeException(e);
                    }
                }else {
    
    
                    Desk.count--;
                    System.out.println("消费者正在消费产品,还需要消费数量:"+ Desk.count);
                    //消费完唤醒生产者继续做
                    Desk.lock.notifyAll();
                    //修改桌子状态
                    Desk.foodFlag = 0;

                }
            }
        }
    }
}

chef

public class Cooker extends Thread{
    
    
    @Override
    public void run() {
    
    
        while (true){
    
    
            if (Desk.count == 0){
    
    
                break;
            }
            synchronized (Desk.lock){
    
    
                if (Desk.foodFlag == 1){
    
    
                    try {
    
    
                        Desk.lock.wait();
                    } catch (InterruptedException e) {
    
    
                        throw new RuntimeException(e);
                    }
                }else {
    
    
                    //修改桌上食物状态
                    Desk.foodFlag = 1;
                    System.out.println("生产者生产了面条");
                    //唤醒消费者
                    Desk.lock.notifyAll();
                }
            }
        }
    }
}

main method

public class TestDemo {
    
    
    public static void main(String[] args) {
    
    
        Eater e = new Eater();
        Cooker c = new Cooker();

        e.setName("消费者");
        c.setName("生产者");

        e.start();
        c.start();
    }
}


important point:
Desk.lock.wait(); //Let the current thread bind to the lock
Desk.Lock.notifyAll(); //Wake up all threads bound to this lock
The reason why the lock object is called here is to avoid the notifyAll() method from waking up all threads (including those outside the method such as system threads, etc.), call with an object, which can indicate which thread is awakened
(here, call with the lock object in the Desk class, because the lock object is unique, and it is only new once in the Desk class)


Thread Pool

insert image description here
Use the thread pool as a container to store threads
①Create a pool, which is empty

②When submitting a task, the pool will create a new thread object. After the task is executed, the thread will be returned to the pool. When submitting the task again next time, there is no need to create a new thread, and the existing thread can be reused directly.

③But if there is no idle thread in the pool when submitting the task, and no new thread can be created, the task will queue up

Executors: The thread pool tool class returns different types of thread pool objects by calling methods.
insert image description here

insert image description here


custom thread pool

The thread pool workflow, it will have the number of core threads, the number of temporary threads, the length of the queue, and idle time, which will affect the decision

When the number of tasks exceeds the number of core threads, redundant tasks will be queued in the queue.
When the number of tasks exceeds the number of core threads and the queue is full, temporary threads will be used to process tasks.
When the number of tasks exceeds the number of core threads, the maximum queue The sum of the number and the number of temporary threads. When fully loaded, redundant tasks will be discarded or processed according to the rejection policy of the thread pool

The idle time of the temporary thread will be destroyed when it exceeds the time specified by the thread pool initialization,
and the core thread will be destroyed only when the thread pool is destroyed


Creating a thread pool will find that there are 7 parameters

insert image description here

The final task rejection strategy has the following

insert image description here

Create a custom thread pool code as follows

insert image description here


Finally, thank you for reading, I hope this article can clear your doubts

Guess you like

Origin blog.csdn.net/giveupgivedown/article/details/128965169