Java's Multithreading Elementary 2

Table of contents

1. Review of the content of the previous section

1. The difference between process and thread

2. Four ways to create threads

2. Code display of the advantages of multithreading

1. Advantages of multithreading

2. Code implementation

3. Commonly used methods of the Thread class

1. The constructor in the Thread class

2. Properties in the Thread class

1. Name the thread and get the name of the thread

2. Demo isDaemon()

3. Demo isAlive()

4. Demo getState()

3. Methods in the Thread class

1. Start a thread---start() method

2. Waiting for a thread---join() method

3. Interrupt a thread ----interrupt() method

3. Get the current thread

4. Dormant current line

5. Actively give up the CPU---yield() method


1. Review of the content of the previous section

The content of the previous section guides the way: Java's multithreading elementary

1. The difference between process and thread

1. The process contains at least one thread (main thread)

2. A process is the smallest unit for applying for computer resources

3. Thread is the smallest unit of CPU scheduling

4. The processes are isolated from each other. The threads use the resources uniformly applied by the processes, and they can influence each other.

2. Four ways to create threads

1. Inherit the Thread class and rewrite the run() method

2. Implement the Runnable interface and rewrite the run() method

3. Create Thread and implement Runnable through anonymous inner class

4. Implement a thread through Lambda expression

2. Code display of the advantages of multithreading

1. Advantages of multithreading

Through the study in the previous section, we know that multithreading can make full use of CPU resources and improve the operating efficiency of the program.

2. Code implementation

public class Demo6_10B {
    public static void main(String[] args) {
        serial();
        concurrent();

    }

    public static long COUNT = 10_0000_0000L;

    //串行执行10亿次的自增
    public static void serial() {
        long start = System.currentTimeMillis();
        long a = 0L;
        for (int i = 0; i < COUNT; i++) {
            a++;
        }
        long b = 0L;
        for (int i = 0; i < COUNT; i++) {
            b++;
        }
        long end = System.currentTimeMillis();

        System.out.println("串行的总耗时为:" + (end - start) + " ms");

    }

    //并行执行10亿次的自增
    public static void concurrent() {
        long start = System.currentTimeMillis();

        Thread thread1=new Thread(()->{
            long a=0L;
            for (int i = 0; i < COUNT; i++) {
                a++;
            }
        });
        Thread thread2=new Thread(()->{
            long a=0L;
            for (int i = 0; i < COUNT; i++) {
                a++;
            }
        });
        thread1.start();
        thread2.start();
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        long end = System.currentTimeMillis();
        System.out.println("并行的总耗时为:" + (end - start) + " ms");
        
    }
}

Note: When implementing parallel code, you must add thread1.join(); and thread2.join(); these two pieces of code, the function of the join() method is to wait for the thread to finish executing, and then continue to execute the following code. If we Without these two pieces of code, the following situation may occur: the self-increment operation of the two threads has not been completed, and the main thread (main) thread has been executed, that is, the total time-consuming final execution is directly printed, and the correct time-consuming The result should be more than half the time taken by serial.

There is no join() code execution result here. You can see that the main thread has finished executing, and thread 1 and thread 2 have not yet finished executing.

Add join() to print the result:

 We execute the code a few more times and find that the time-consuming of each parallel is more than half of the serial time-consuming, so why not half? In fact, it is not difficult to think carefully: the creation and destruction of threads also take a certain amount of time , so it is always more than half.

Next, we will change the value of COUNT to a smaller point, for example, to COUNT=10_000L, and then we will execute

It is found that the total time-consuming of serial is shorter than the total time-consuming of parallel, so we can boldly speculate that not all scenarios have the highest efficiency of multi-threading. When our computation load is small, creating threads The time is shorter than the running time of the code, which is obviously not suitable for multi-threading.

3. Commonly used methods of the Thread class

1. The constructor in the Thread class

method illustrate
Thread() create a thread object
Thread(String name) Create a thread object and name it
Thread(Runnable target) Create threads using Runnable objects
Thread(Runnable target,String name) Create a thread using the Runnable object and name it
【Understand】 Thread(ThreadGroup group,
Runnable target)
Threads can be used for group management, and the divided groups are thread groups, which we can understand at present

Thread t1 = new Thread();

Thread t2 = new Thread ( new MyRunnable ());
Thread t3 = new Thread ( " This is the thread name " );
Thread t4 = new Thread ( new MyRunnable (), "This is the thread name" );

2. Properties in the Thread class

Attributes
access method
ID
getId()
name getName()
state getState()
priority getPriority()
Whether background thread isDaemon()
whether to survive isAlive()
Is it interrupted isInterrupted()

1. Name the thread and get the name of the thread

public class Demo7_ThreadName {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (true) {
                System.out.println(Thread.currentThread().getName() + ":hello Thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "这是thread线程");
        thread.start();

    }
}

 View thread name in jconsole

 When we do not execute the thread name, the system will automatically generate the thread name, starting from Thread0 and generating it backwards

2. Demo isDaemon()

The threads we used before are all foreground threads, because after creating threads, they are all foreground threads by default, and must be manually set as background threads. We use thread.setDaemon(true); code to set this as a background process

public class Demo8_Daemon {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            while (true) {
                System.out.println(Thread.currentThread().getName() + ":hello Thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        //手动设置为后台线程,默认为false,也就是前台线程
        thread.setDaemon(true);
        System.out.println("是否存活:" + thread.isAlive());
        // 启动线程
        thread.start();
        // 休眠一会,确保系统PCB创建成功
        Thread.sleep(500);
        System.out.println("是否存活:" + thread.isAlive());
        System.out.println("main线程执行完成");
        System.out.println("是否存活:" + thread.isAlive());
        
    }
}

The printed results are as follows:

 We can see that with the completion of the main method, the program is also closed

Compared with the foreground process, the results printed by the foreground process are as follows:

 So we can summarize the scenarios where the foreground process and the background process run

Foreground process: Use the foreground process in some transactions that require precise business and low fault tolerance rate, such as bank transfer, to ensure the completion of the transfer thread.

Background process: In some tasks with a high fault tolerance rate, it can be used, such as WeChat's step count calculation, which does not need to accurately calculate your precise step count, and does not affect the shutdown of the main thread..

3. Demo isAlive()

public class Demo9_IsAlive {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            int cnt = 0;
            while (true) {
                System.out.println("hello thread.....");
                cnt++;
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                if (cnt == 5)
                    break;
            }
        });

        System.out.println("是否存活:" + thread.isAlive());
        // 启动线程
        thread.start();

        System.out.println("是否存活:" + thread.isAlive());
        //等待线程执行完毕
        try {
            thread.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("是否存活:" + thread.isAlive());

    }
}

The printed results are as follows:

From this we can conclude that: the isAlive() method is to determine whether the system thread (PCB) is alive , not our new Thread object

4. Demo getState()

  • NEW : Create a Java thread (object), but the start() method has not been called yet, that is, it does not participate in CPU scheduling. At this time, it is a Java object
  • RUNNABLE : Running or in the ready queue (PCB's ready queue)
  • BLOCKED : Enter the blocked state (synchronized) while waiting for the lock
  • WAITING : waiting with no time limit
  • TIMED_WAITING : Waiting for a period of time (waiting with a time limit) (not waiting for expiration)
  • TERMINATED : The thread execution is completed, the PCB has been destroyed in the operating system, but the JAVA object is still there

Transition diagram between thread states

 Here to observe three states NEW-->TIME_WAITING-->TERMINATED

public class Demo14_State {
    public static void main(String[] args) {
        Thread thread=new Thread(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        System.out.println("启动之前的状态:"+thread.getState());

        thread.start();
        //等待线程的创建
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("启动之后的状态:"+thread.getState());
        //等待线程结束
        try {
            thread.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("线程完成之后的状态:"+thread.getState());

    }
}

The printed result is: 

  Here to observe three states NEW-->RUNNABLE-->TERMINATED

public class Demo14_State {
    public static void main(String[] args) {
        Thread thread=new Thread(()->{
            for(long i=0;i<10000000000L;++i){

            }
        });
        System.out.println("启动之前的状态:"+thread.getState());

        thread.start();
        //等待线程的创建
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("启动之后的状态:"+thread.getState());
        //等待线程结束
        try {
            thread.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("线程完成之后的状态:"+thread.getState());

    }
}

The printed results are as follows:

The specific blocked state BLOCK (the specific usage of synchronized is in the next section)

public class Demo16_State2 {

    public static void main(String[] args) {
        final Object object = new Object();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                //此时object对象就是锁
                synchronized (object) {
                    while (true) {
                        System.out.println("张三");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }, "t1");
        t1.start();
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("李四");
                    System.out.println("hehe");
                }
            }
        }, "t2");
        t2.start();
        //等待线程的创建
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("线程1此时的状态:"+t1.getState());
        System.out.println("线程2此时的状态:"+t2.getState());

    }

}

Because the state of thread one is sleep(1000), it is now TIMED_WAITING, because thread one has got the lock, which is an object object, but thread two has not got it, and it needs to wait until thread one releases the lock before proceeding, so thread two's Status is BLOCK

Change the Thread.sleep(1000) code of the above code to object.wait(); the code can observe the WAIT state

The specific printing is as follows. You can see that thread one is in the WAIT state, and each thread continues to print downwards, and the lock is not released, so the second thread is in the BLOCK state.

in conclusion:

1.BLOCKED means waiting to acquire a lock , WAITING and TIMED_WAITING means waiting for notifications from other threads .
2. The TIMED_WAITING thread is waiting to wake up, but a time limit is set ; the WAITING thread is waiting to wake up indefinitely

3. Methods in the Thread class

1. Start a thread---start() method

We talked about the start method in the previous section, and start a thread by calling the start0() local method

With the above foundation, here we analyze the difference between the start() method and the run() method

1. The start() method is to actually apply for a system thread, and the run() method is to define the tasks to be performed by the thread

2. Call the run() method directly, instead of applying for a real system thread (PCB), but call the method of the object

Call the start() method, and the JVM will call the local method to apply for a real system thread (PCB), and execute the logic in the run() method

Next, use the following code to better understand:

public class Demo13_StartRun {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (true) {
                System.out.println("hello thread...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        });
        thread.run();
//        thread.start();
        System.out.println("线程状态:" + thread.getState());
        System.out.println("主线程结束");

    }
}

The result printed by calling the run() method:

The result printed by calling the start() method:

It can be observed that calling the run() method does not print the end of the main thread at all, while calling the start() method does.

2. Waiting for a thread---join() method

The role of the join() method: wait for the end of the current thread to proceed to the next step

method illustrate
public void join()
wait for thread to end
public void join(long millis) Wait for the thread to end, at most millis millis milliseconds
public void join(long millis, int nanos) Same, but with higher precision
public class Demo12_Join {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 5; ++i) {
                System.out.println("hello,thread " + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        thread.start();

        System.out.println("join之前,线程状态:" + thread.getState());
        System.out.println("join之前,是否存活:" + thread.isAlive());
        // 使用join等待thread线程结束
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("join之后,线程状态:" + thread.getState());
        System.out.println("join之后,是否存活:" + thread.isAlive());
        System.out.println("主线程执行完成。");


    }
}

Printed result:

3. Interrupt a thread ----interrupt() method

Thread interruption: Stop or interrupt the task of the current thread.

There are two ways to implement thread interruption:

1. Whether to pass the interrupt flag (manually)

Interrupt the thread by modifying isQuit

public class Demo10_Interrupted {
    public static boolean isQuit = false;

    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (!isQuit) {
                System.out.println("hello thread...");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
            System.out.println("线程任务完成");
        });

        // 启动线程
        thread.start();
        //让子线程先执行一段时间
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        //通过修改isQuit来中断线程
        isQuit = true;
        //让子线程先结束
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("主线程任务完成");

    }
}

The printed results are as follows:

2. Interrupt the thread through the interrupted() method provided by the Thread class

public class Demo11_Interrupted02 {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("hello thread...");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
            System.out.println("是否中断:" + Thread.currentThread().isInterrupted());
            System.out.println("线程任务完成");
        });

        // 启动线程
        thread.start();
        //让子线程先执行一段时间
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        //中断线程,修改Thread的中断标志
        thread.interrupt();
        //让子线程先结束
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("主线程任务完成");
    }
}

When we use the above code to interrupt the thread, the printed results are as follows:

 Because the current state of the sub-thread is in sleep() or blocked state, the interrupted state of the thread at this time is sleep() or blocked state. For example, in the real situation, Zhang San studies for 1 second and sleeps for 1 minute. We interrupt Zhang San's state. There is a high probability that Zhang San is sleeping, and what we interrupt is Zhang San's sleeping state.

So how to interrupt the thread? When we see the printed result, we see that there is an exception capture. In fact, we can think of adding a break when the exception is caught.

public class Demo11_Interrupted02 {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("hello thread...");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    break;
                }

            }
            System.out.println("是否中断:" + Thread.currentThread().isInterrupted());
            System.out.println("线程任务完成");
        });

        // 启动线程
        thread.start();
        //让子线程先执行一段时间
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        //中断线程,修改Thread的中断标志
        thread.interrupt();
        //让子线程先结束
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("主线程任务完成");
    }
}

3. Get the current thread

method illustrate
public static Thread currentThread();
Returns a reference to the current thread object

public class ThreadDemo {
    public static void main(String[] args) {
        //获取到的是主线程main
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName());
   }
}

4. Dormant current line

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

The sleep method only guarantees to sleep within this time period, and is not scheduled to run on the CPU, which is not very accurate.

5. Actively give up the CPU---yield() method

public class Demo15_Yield {
    public static void main(String[] args) {
        Thread thread1=new Thread(()->{
            while (true){
                System.out.println("我是张三");
//                Thread.yield();
            }
        });
        Thread thread2=new Thread(()->{
            while (true){
                System.out.println("我是李四");
            }
        });
        thread1.start();
        thread2.start();

    }
}

When the print of the yield() method is commented out, it is basically half and half of the print.

When using the yield() method, it is obvious that the printing of Li Si is larger than that of Zhang San

Conclusion: yield does not change the state of the thread , but it will requeue

Guess you like

Origin blog.csdn.net/qq_64580912/article/details/130516625