An article in-depth understanding of java thread

In-depth understanding of java thread

We know that one thread can be used to perform a task, and the task execution is asynchronous and does not block the code behind. In a java process, contains the main method of the class is executed in a thread. In practice, if a process requires time-consuming operation, in order not to affect the overall response procedures, this will usually a time-consuming operations in a thread, performed asynchronously. However, the thread is how to achieve asynchronous execution of tasks it? This article will understand the Thread class, in order to obtain the desired secret thread of execution.

According to "in-depth understanding of JAVA virtual machine" section on the thread, we learned that a corresponding thread Thread operating system in java. The operating system thread is a scarce resource, can not be unlimited create a thread, which is one of the reasons why the use of thread pool.

We also know that in order to achieve a thread in java, there are two ways:

  • Thread class inheritance
  • Implement Runnable

Either way, however, still have to perform the last thread by calling the Thread's start () method

Let's look at the important properties and methods of the Thread class:

// target就是一个传递给Thread等待Thread执行的Runnable对象
/* What will be run. */
private Runnable target;

/* The group of this thread */
private ThreadGroup group;

// 类方法,该方法会在Thread类初始化时,在类的构造器<clinit>中被调用,且只会调用一次,该方法主要的作用是注册一些本地的方法,以便后期可以使用
/* Make sure registerNatives is the first thing <clinit> does. */
private static native void registerNatives();
static {
    registerNatives();
}
// 注册了以下本地方法:
public static native Thread currentThread();
public static nativevoid yield();
public static native void sleep(long millis) throws InterruptedException;
private native void start0();
private native boolean isInterrupted(boolean ClearInterrupted);
public final native boolean isAlive();
public static native boolean holdsLock(Object obj);
private native static StackTraceElement[][] dumpThreads(Thread[] threads);
private native static Thread[] getThreads();
private native void setPriority0(int newPriority);
private native void stop0(Object o);
private native void suspend0();
private native void resume0();
private native void interrupt0();
private native void setNativeName(String name);
复制代码

Let's look at what this code will output:

public static class MyThread extends Thread{
    @Override
    public void run(){
        System.out.println("MyThread---1");
    }
}
public static class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("MyRunnable---1");
    }
}
public static void main(String[] args) {
    Thread t1 = new MyThread();
    Thread t2 = new Thread(new MyRunnable());
    t1.start();
    t2.start();
    System.out.println("MyThread---2");
    System.out.println("MyRunnable---2");
}
复制代码

Output the contents of the code is uncertain and may output:

MyThread---2
MyRunnable---2
MyRunnable---1
MyThread---1
复制代码

Output is also possible:

MyThread---1
MyRunnable---1
MyThread---2
MyRunnable---2
复制代码

However, if the above code t1.start (), t2.start () to:

t1.run();
t2.run();
复制代码

Then the output becomes determined:

MyThread---1
MyRunnable---1
MyThread---2
MyRunnable---2
复制代码

Why start (), the contents of the output is uncertain, and the use of run () output is to determine it? This process needs to start from the beginning to understand the Thread. The start () method of the Thread class source code is as follows:

public synchronized void start() {
    if (threadStatus != 0)
        throw new IllegalThreadStateException();

    /* Notify the group that this thread is about to be started
     * so that it can be added to the group's list of threads
     * and the group's unstarted count can be decremented. */
    group.add(this);
    boolean started = false;
    try {
        start0();
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
            /* do nothing. If start0 threw a Throwable then
              it will be passed up the call stack */
        }
    }
}
private native void start0();
复制代码

It can be seen inside the start () method actually calls the method start0 a native of (). When the Thread class initialization, execute a method RegisterNatives () to register the local method, wherein the method start0 practical relevance is JVM_StartThread method:

{"start0", "()V",(void *)&JVM_StartThread}
复制代码

In jvm.cpp, the following code segment:

JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)){
    ...
    native_thread = new JavaThread(&thread_entry, sz);
    ...
}
复制代码

Here JVMENTRY is a macro that defines JVMStartThread function, you can see to create a truly thread-local platform-specific functions within its thread function is thread_entry, as follows:

static void thread_entry(JavaThread* thread, TRAPS) {
    HandleMark hm(THREAD);
    Handle obj(THREAD, thread->threadObj());
    JavaValue result(T_VOID);
    JavaCalls::call_virtual(&result,obj,KlassHandle(THREAD,SystemDictionary::Thread_klass()),
    vmSymbolHandles::run_method_name(),    //调用了run_method_name
    vmSymbolHandles::void_method_signature(),THREAD);
}
复制代码

You can see the call vmSymbolHandles :: runmethodname method, and runmethodname in vmSymbols.hpp with macro definitions:

class vmSymbolHandles: AllStatic {
    ...
    // 这里决定了调用的方法名称是 “run”
    template(run_method_name,"run")
    ...
}
复制代码

From the above code, the Thread execution start () method, first creates a new operating system thread, the thread is then obtained when the operating CPU time slice, performs a callback method: run (), which you can create as evidenced by the start () a new thread and asynchronous thread of execution body through the run () method only to perform ordinary thread object only, and will not be executed in parallel, but serial execution.

The following are sending you some of the common problems associated with Thread:

Thread的sleep、join、yield

  • 1.sleep
  1. sleep () makes the current thread into the stagnant state (blocks the current thread), so that the CPU usage to leave some time for other threads
  2. When sleep will not release the object lock Sleep
  • 2.join executed in one thread in a join method A thread B, then A will be suspended, waiting for B is finished after the implementation of the follow-up tasks
public static void main(String[] args){
    Thread t1 = new Thread();
    t1.start();
    t1.join();
    // 以下代码会在t1执行完毕后打印
    System.out.println("t1 finished");
}
复制代码
  • 3.yield
  1. yield does not mean exit and pause, but, if someone needs to tell the thread scheduling, you can take it, I too will perform again, no one needs, I continue
  2. Call yield when the lock has not been released

object的wait、notify、notifyAll

  • 1.wait
  1. wait () method is a method in the class Object; performed when a thread to wait () method when
  2. The thread enters a wait and objects related to the pool, while losing the lock object, to acquire a lock to be awakened again
  3. wait () using notify () or notifyAll () or specify a sleep time to wake up threads that are currently waiting in the pool
  4. wait () must be placed in synchronized block, otherwise it will error "java.lang.IllegalMonitorStateException"
  • 2.notify

wait () and notify () must operate with a "subject Monitor"

Runnable1 implements Runnable{
    public void run(){
        synchronized(lock){
            // 等待其他线程来唤醒
            lock.wait();
            System.out.println("Runnable1 has been notified by other thread");
        }
    }
}
Runnable2 implements Runnable{
    public void run(){
        synchronized(lock){
            System.out.println("Runnable2 will notify other thread who wait for lock");
            // 唤醒其他线程            lock.notify();
        }
    }
}
public static void main(String[] args){
    Object lock = new Object();
    Thread t1 = new Thread(new Runnable1(lock));
    Thread t2 = new Thread(new Runnable2(lock));
    t1.start();
    t2.start();
}
复制代码

Thread and Runnable of difference

  • Runnable by Thread's start (actually called the run method of the target) starts, while Thread the target attribute is a Runnable
  • Runnable properties can be achieved resource sharing, Thread can not share resources
MyRunnable r = new MyRunnable();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
// t1/t2线程操作的都是同一个实例r,所以r中的数据可以实现多线程共享
t1.start();
t2.start();
复制代码

How to communicate between threads

  • the Join : a thread wait for another thread is finished before execution
  • the wait / the Notify : a thread wait for another thread wakes you own an object monitor after execution
  • CountdownLatch : a thread to wait (countDownLatch.await ()) any number of other thread execution is completed after (countDownLatch.countDown ()) then execute
  • CyclicBarrier : All threads before their preparation, unity began to follow up when all threads are ready (all calls cyclicBarrier.await ())
  • Semaphore : you can control the number of threads simultaneously access, obtain a license by acquire (), if not wait, and release () Releases a permit
  • A Callable : sub-thread execution results back to the parent thread
FutureTask<Integer> futureTask = new FutureTask<>(callable);
new Thread(futureTask).start();
Object result = futureTask.get();
复制代码
  1. CountDownLatch and are able to achieve CyclicBarrier wait between threads, but they have different emphases:
  2. After CountDownLatch A generally used for a thread to wait several other threads executing the task, it was executed;
  3. CyclicBarrier generally used for a group of threads wait for each other to a certain state, and then execute that group of threads simultaneously;
  4. In addition, CountDownLatch is not able to be reused, and CyclicBarrier can be reused.
  5. In fact, Semaphore and lock somewhat similar, it is generally used to control access to a certain set of resources.

Principle thread pool

Thread pool has two parameters: the number of kernel threads and the maximum number of threads coreNum maxNum

Assume initialize a thread pool, the core number of threads is 5, the maximum number of threads is 10, initialize the thread pool when there is no thread

When came after a task, you initialize a thread, if a job again, and then initialize a thread, a continuous thread initialized 5, if the first six tasks coming

Then will the sixth mission into the blocking queue

Now has five threads in the thread pool, if one thread is idle, it will get the first six task from blocking queue for execution

If five thread pool threads are running the state, then the first task is stored in the blocking queue

If the queue is full, and we set the maximum number of threads is 10, but only five threads in the pool, then creates a new thread to execute the blocking can not be saved to the task queue, this time with six thread pool thread

If the number of threads in the pool reached 10, and and also blocking queue is full, you can go to these tasks by custom reject function

After the last run for a period of time, blocking the queue of tasks execution is over, the number of threads in the thread pool exceeds the core thread is automatically recovered within a period of idle time

Guess you like

Origin juejin.im/post/5e144acaf265da5d381d114f