Basic operation of thread

qzlzzz 2021-10-14 05:08:10

Preface

The basis of high concurrency development is to see how well developers control threads , Thread safety , Data security and efficiency optimization of one request processing , This article is about how to simply operate threads , At the same time, it leads to the explanation of the next article on thread pool .

In the following example program , I will use many tool class methods to avoid code redundancy and high coupling . If there are too many tool class methods, they will be presented in the form of scalable code blocks :

Tool class definition

public class ThreadUtil {
static public class CustomThreadFactory implements ThreadFactory {
private AtomicInteger threadNo = new AtomicInteger(1);
private String threadName;
private String tmp;
public CustomThreadFactory(String name){
this.tmp = name;
}
@Override
public Thread newThread(Runnable r) {
this.threadName = this.tmp+threadNo.get();
threadNo.incrementAndGet();
Print.tco(" Created a new thread , The name is : "+this.threadName);
Thread thread = new Thread(r,threadName);
return thread;
}
}
public static void shutdownThreadPoolGracefully(ExecutorService threadPool){
// If it has been closed, return to
if (!(threadPool instanceof ExecutorService) || threadPool.isTerminated()){
return;
}
try {
threadPool.shutdown();
}catch (SecurityException e){
return;
}catch (NullPointerException e){
return;
}
try {
// wait for 60 second , Wait for tasks in the thread pool to complete execution
if (!threadPool.awaitTermination(60,TimeUnit.SECONDS)){
threadPool.shutdownNow();
if (threadPool.awaitTermination(60,TimeUnit.SECONDS)){
System.err.println(" Thread pool task did not end normally ");
}
}
} catch (InterruptedException e) {
threadPool.shutdownNow();
}
try {
if (!threadPool.isTerminated()) {
for (int i = 0; i < 100; i++) {
if (threadPool.awaitTermination(10,TimeUnit.MILLISECONDS)){
break;
}
threadPool.shutdownNow();
}
}
}catch (InterruptedException e){
System.err.println(e.getMessage());
}catch (Throwable e){
System.err.println(e.getMessage());
}
}
// Lazy singleton to create thread pool : Used to perform timing , Sequential execution
static class SeqOrScheduledTargetThreadPoolLazyHolder{
static final ScheduledThreadPoolExecutor EXECUTOR = new ScheduledThreadPoolExecutor(1,
new CustomThreadFactory("seq"));
static {
Runtime.getRuntime().addShutdownHook(
new ShutdownHookThread(" Timed and sequential task thread pool ",()->{
shutdownThreadPoolGracefully(EXECUTOR);
return null;
})
);
}
}
//CPU Check the number
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
//IO Number of processing threads
private static final int IO_MAX = Math.max(2,CPU_COUNT*2);
/**
* Idle life time limit , Unit second
*/
private static final int KEEP_ALIVE_SECONDS = 30;
/**
* Bounded queues size
*/
private static final int QUEUE_SIZE = 128;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT;
// Lazy singleton to create thread pool : be used for IO Intensive task
private static class IoIntenseTargetThreadPoolLazyHolder{
private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(
IO_MAX,
IO_MAX,
KEEP_ALIVE_SECONDS,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(QUEUE_SIZE),
new CustomThreadFactory("io")
);
static {
EXECUTOR.allowCoreThreadTimeOut(true);
Runtime.getRuntime().addShutdownHook(
new ShutdownHookThread("IO Intensive task thread pool ", new Callable() {
@Override
public Object call() throws Exception {
shutdownThreadPoolGracefully(EXECUTOR);
return null;
}
}));
}
}
// Lazy singleton to create thread pool : be used for CPU Intensive task
private static class CpuIntenseTargetThreadPoolLazyHolder{
private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(
MAXIMUM_POOL_SIZE,
MAXIMUM_POOL_SIZE,
KEEP_ALIVE_SECONDS,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(QUEUE_SIZE),
new CustomThreadFactory("cpu")
);
static {
EXECUTOR.allowCoreThreadTimeOut(true);
Runtime.getRuntime().addShutdownHook(new ShutdownHookThread("CPU Intensive task thread pool ", new Callable() {
@Override
public Object call() throws Exception {
shutdownThreadPoolGracefully(EXECUTOR);
return null;
}
}));
}
}
private static final int MIXED_MAX = 128;// Maximum thread
private static final String MIXED_THREAD_AMOUNT = "thread.amount";
private static class MixedTargetThreadPoolLazyHolder{
// Start with the environment variable thread.amount Get the number of preconfigured threads in
// If there is no right thread.amount To configure , Just use constants MIXED_MAX As the number of threads
private static final int max = (null != System.getProperty(MIXED_THREAD_AMOUNT) ?
Integer.parseInt(System.getProperty(MIXED_THREAD_AMOUNT)) : MIXED_MAX);
public static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(
max,
max,
KEEP_ALIVE_SECONDS,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(QUEUE_SIZE),
new CustomThreadFactory("mixed")
);
public MixedTargetThreadPoolLazyHolder(){
System.out.println("Mix Class creation ");
}
static {
EXECUTOR.allowCoreThreadTimeOut(true);
Runtime.getRuntime().addShutdownHook(
new ShutdownHookThread(" Hybrid task thread pool ", new Callable() {
@Override
public Object call() throws Exception {
shutdownThreadPoolGracefully(EXECUTOR);
return null;
}
})
);
}
}
public static ThreadPoolExecutor getMixedTargetThreadPool(){
return ThreadUtil.MixedTargetThreadPoolLazyHolder.EXECUTOR;
}
public static String getCurThreadName(){
return Thread.currentThread().getName();
}
public static void sleepMilliSeconds(int millisecond){
LockSupport.parkNanos(millisecond*1000L*1000L);
}
public static void execute(String cft){
synchronized (System.out){
System.out.println(cft);
}
}
public static Thread getCurThread(){
return Thread.currentThread();
}
public static Boolean getCyrThreadDaemon(){
return getCurThread().isDaemon();
}
public static void sleepSeconds(int second){
LockSupport.parkNanos(second * 1000L * 1000L * 1000L);
}
}

If the reader wants to demonstrate each function with the author's code , It is suggested that the form of guide package is :
import static *.ThreadUtil.*;

In a tool class ShutdownHookThread class :

package com.qzlnode.util;
import java.util.concurrent.Callable;
public class ShutdownHookThread extends Thread{
private final Callable callable;
private volatile boolean target = false;
public ShutdownHookThread(String name, Callable callable) {
super(name);
this.callable = callable;
}
@Override
public void run() {
synchronized (this){
try {
if (!target){
target = true;
callable.call();
}
}catch (Exception e){
System.err.println(e.getMessage());
}
}
}
}

Set the name for the thread

Thread Class provides three ways to set the name of a thread :

  • When creating a new thread , Can pass Thread Class constructor to initialize the name of the new thread .
  • You can create a new thread , Use... Before starting the thread Thread Class provides the setName Method to set the name for the thread .
  • When neither of the previous two methods is used , The system will automatically set the name for the thread . The default is Thread-i.i The sequence number of the threads created for you

When a thread executes a logical block of user code , Code logic blocks can have setName() Method , Allow the thread to run while , Dynamically sets the name of the currently running thread .

It is not recommended to give threads the same name , The thread name mentioned in the previous article is actually for users , The same name is easy to use by users jstack The tool is confused when viewing threads .

Examples of simple thread name operations

public class ThreadNameDemo {
private static int SLEEP_GAP = 500;
static class RunTarget implements Runnable{
@Override
public void run() {
Print.tco(getCurThreadName()+" doing~~~");
sleepMilliSeconds(SLEEP_GAP);
Print.tco(getCurThreadName()+" Execution completed .");
}
}
public static void main(String[] args) {
RunTarget target = new RunTarget();
new Thread(target).start();
new Thread(target).start();
new Thread(target, " Threads -A").start();
new Thread(target," Threads -B").start();
sleepMilliSeconds(Integer.MAX_VALUE);
}
}

We can look at the results :

When we set the name of the new thread in the way of constructor , We get the current thread name in the running thread , You will see the name we set ourselves , But when we don't set the name for the new thread , What we get is the name that the system automatically assigns to us :Thread-i.

Let the thread take a break

If you've ever been in contact with thread pools , You know that every logical block of user code is a job , I like to think of threads as assembly line employees , It deals with the work in hand tirelessly , Only the supervisor ( The scheduler ) Don't give it to keep working ( Did not get CPU Time slice ) 了 , It just stopped, but the heart is still looking forward to the next assignment to deal with the work at hand . Threads are so tired , Why don't we let it rest artificially .

sleep The function of is to make the currently executing thread sleep , In terms of thread state, it is changing from execution state to time limited waiting state . We from Thread Check it out. sleep How many uses are there .

  • public static native void sleep(long millis) throws InterruptedException;
  • public static void sleep(long millis,int nanos) throws InterruptedException;

from Thread Class, we can see that there are two sleep Overloading methods , One is to let the thread rest for a user-defined millisecond time , The other is to let the thread rest for a user-defined time of milliseconds and seconds . At the same time, we can see that each method throws InterruptedException abnormal , This also requires us to catch and handle this exception .

The thread is in sleep When , Thread state is in time limited waiting state , In the waiting state , The operating system does not allocate... To threads CPU Time slice , And time ends , There is no such thing as interrupt Wake up operation , The thread state will automatically change to ready state , Wait for the operating system to assign CPU Time slice .

Next, let's write an example :

public class Sleep {
static class SleepThread extends Thread{
static int threadNo = 1;
public SleepThread() {
super("thread-"+threadNo);
threadNo++;
}
@Override
public void run() {
Print.tco(" This thread is going to sleep ~~~"+getState());
sleepSeconds(10);// Each thread breaks 10 second
Print.tco(" The thread fell asleep ~~~"+getState());
}
}
public static void main(String[] args) {
Thread thread = null;
for (int i = 0; i < 5; i++) {
thread = new SleepThread();
thread.start();
}
Print.tco(getCurThreadName()+" end of execution .");
}
}

When the program is executed , We use Jstack Tool View JVM Thread state in , From the first picture below, we can clearly see that the thread is in TIME_WAITING state , As the program runs , We found that the output in the console shows that each thread is Runnable state . This is normal , Only threads get CPU Time slice will run Print.tco() sentence , The thread at this time must be running .

But it should be noted that : When the thread break time is up , The state of the thread should be ready , Wait until the operating system gives the thread CPU Time slice will output The thread fell asleep This statement , At this time, the state of the thread must also be running (Runnable).

Wake up the thread's dream

Ignore thread pool technology , When does a thread exit after execution ? This problem is only known by the thread itself , When we accidentally don't set the wake-up time for the thread , When can the thread exit WAITING State ? Now let's introduce interrupt Method ,interrupt And stop The difference is :

  • stop The method is like unplugging your computer when you write an article , It can lead to data insecurity , Data inconsistency .
  • interrupt The method does not directly interrupt the thread, but sets the thread to the interrupt flag bit , The user can cycle through this state , Then make the corresponding treatment .

Now let's explain two scenarios :

(1) When the thread is in BLOACKED In the state of , Use interrupt Method , Will exit immediately , And throw InterruptedException abnormal , At this point, we can catch the exception and do some processing , Then let the thread exit . Of course, the thread must be ready to handle InterruptedException Abnormal preparation . Generally speaking, when we call infinite sleep、wait、join In operation , The thread will enter a blocked state .

Why use interrupt Method only sets the interrupt flag bit, but it can make the block exit , And throw InterruptException abnormal , We from JDK Source code answer :

 public void interrupt() {
if (this != Thread.currentThread()) {
checkAccess();
// thread may be blocked in an I/O operation
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupted = true;
interrupt0(); // inform VM of interrupt
b.interrupt(this);
return;
}
}
}
interrupted = true;
// inform VM of interrupt
interrupt0();
}
  • First of all to enter interrupt Method , It will judge whether the thread object you want to wake up is equal to the currently obtained CPU Thread object that the time slice is running , If not, first obtain the security management file and check your permissions .
 private volatile Interruptible blocker;
private final Object blockerLock = new Object();
  • Get the lock after ( We can see blockerLock Be being final Embellished , That is to say, the following thread needs to access blockerLock Variable words , Will go into a blocking state ), Execute a synchronized block of code , Will give b Assign one Interruptible Type of bloack.( We can also see that block By volatile Embellished , It's a lightweight lock , It ensures the visibility of shared variables in multithreading .)

What is? Interruptible type , from JDK In the comments, we know that it is interruptible and has interruptible I/O The object of the thread of the operation .sleep、wait、join The method is interruptible I/O operation , So the b It's not empty .

  • Due to the use of infinite time sleep、wait、join Wait for interruptible I/O Operation method , therefore b Not empty , Get into if Code block , First of all, will boolean Type of interrupted Set to ture, It is convenient to clear the interrupt flag bit later . After the call interrupt0 Method to set the interrupt flag bit , Last call Interruptible Interface interrupt Method to actually interrupt the thread , And receive a InterruptedException abnormal , That is, the thread will throw a InterruptedException abnormal .

(2) Read from the source code above , We know that when the thread runs normally , Use interrupt Method , Only the interrupt flag bit will be set , It won't get in if Code block . So the user program can call... In place isInterrupted() Method to see if you are interrupted , And exit the thread .

Here we can use an example to demonstrate :

 @Test
@DisplayName(" The user program automatically detects Interrupt Property to determine whether to exit the thread ")
public void testInterrupted2() throws InterruptedException {
Thread thread = new Thread(){// An anonymous class
@Override
public void run() {
Print.tco(" I got up ");
// Cycle all the time
while (true){
Print.tco(isInterrupted());
sleepMilliSeconds(5000);
if (isInterrupted()){
Print.tco(" I was caught in seconds ");
return;
}
}
}
};
thread.start();
sleepSeconds(2);// wait for 2 second
thread.interrupt();// Interrupt threads
sleepSeconds(2);// wait for 2 second
thread.interrupt();
}

here @Test Use yes Junit5 edition , This version is more functional , It is also compatible with other detection engines .

Two threads working together

Sometimes a thread depends on other threads to complete its work , The specific dependencies are : A thread needs to merge the execution process of another thread into its own execution process , This involves thread merging . stay Thread Class provides join Method to merge threads , We enter Thread Class to see the definition of its methods :

  • public final synchronized void join(final long millis) throw InterruptedException;
  • public final synchronized void join(long millis, int nanos) throw InterruptedException;
  • public final void join() throws InterruptedException;

We can see that there are three overloaded join() Method .

  • Call the first method , The calling thread will enter TIME_WAITING state , Until the merged thread completes its own task or waits for the merged thread to execute mills( millisecond ) Time for , The calling thread will return to the ready state , Until assigned to CPU Time slice , Will execute the logic block of user code behind itself .
  • Call the second method , The calling thread will enter TIME_WAITING state , Until the merged thread completes its own task or waits for the merged thread to execute mills( millisecond )+nanos( nanosecond ) Time for , The calling thread will return to the ready state , Until assigned to CPU Time slice , Will execute the logic block of user code behind itself .
  • Call the third method , The calling thread will enter WAITING state , Until the merged thread completes its own task , The calling thread will return to the ready state , Until assigned to CPU Time slice , Will execute the logic block of user code behind itself .

join() Method is instance method , You need to use the handle of the merged thread ( Reference object ) To call .

Let's write a simple code to demonstrate its function .

public class Join {
public static final int SLEEP_GAP = 5000;// Sleep duration
public static final int MAX_TURN = 50;// Sleep times
static class SleepThread extends Thread {
static int threadNo = 0;
public SleepThread() {
super("sleepThread-" + threadNo++);
}
@Override
public void run() {
Print.tco(getName() + " Sleep .");
sleepMilliSeconds(SLEEP_GAP);
Print.tco(getName() + " end of execution .")
}
public static void main(String[] args) {
Thread thread1 = new SleepThread();
Print.tco(" start-up tread1");
thread1.start();
try {
thread1.join();// Merge threads 1, No time limit
} catch (InterruptedException e) {
e.printStackTrace();
}
Print.tco(" start-up thread2");
Thread thread2 = new SleepThread();
thread2.start();
try {
thread2.join(1000);// Limited time consolidation , Time limit 1 second
} catch (InterruptedException e) {
e.printStackTrace();
}
Print.tco(" End of thread run .");
}
}

Threads that give up opportunities

The currently executing thread sacrifices itself , Think your task is not so important , Or run to a certain stage and find that the current task does not meet your ideals , So he offered to give up CPU Time slice , Make yourself ready , At this time, the scheduler will re select a thread from the threads to allocate the thread left by the thread with the spirit of self sacrifice CPU Time slice . Please note that , The sacrificial thread is not lost to be allocated by the operating system CPU Time slice right , in other words , Even if it gives up the opportunity , When the operating system reschedules threads , There is also a certain chance that it will continue to be allocated to the person who sacrificed himself CPU Time slice .

in other words ,yield() Operation and sleep(),wait(),join() The operation is different , It does not cause the current thread to enter a time limited wait state , Instead, it becomes ready , Still have been assigned to CPU The power of time slice .

This yield() Method in Thread There is only one version in the class , So readers can try to write programs to experience yield() The characteristics and functions of the method , The code written by the author is not posted here .

redis Single threaded architecture should be written out tomorrow or the day after tomorrow .

Please bring the original link to reprint ,thank
Similar articles

2021-10-14