자바 스레드의 이해 심층 기사

심층 자바 스레드의 이해

우리는 하나 개의 스레드가 작업을 수행 할 수 있다는 것을 알고, 및 작업 실행은 비동기 뒤에 코드를 차단하지 않습니다. 자바 방법에서, 클래스의 주요 방법은 스레드 실행 포함한다. 실제로, 경우 프로세스가 전체 응답 절차에 영향을 미치지 않기 위해, 시간이 많이 걸리는 작업을 필요로, 이것은 일반적으로 스레드에서 시간이 많이 걸리는 작업은 비동기 적으로 수행됩니다. 그러나, 스레드가 작업 그것의 비동기 실행을 달성하는 방법은? 이 문서에서는 실행 원하는 비밀 스레드를 얻기 위해, Thread 클래스를 이해할 것이다.

에 따르면, "깊이있는 자바 가상 머신의 이해"스레드 섹션, 우리는 자바에 해당하는 스레드 스레드 운영 체제 것을 배웠다. 운영 체제 스레드는 무제한는 이유 중 하나입니다 스레드를 만들 수 없습니다, 부족한 자원 왜 스레드 풀의 사용.

우리는 또한 자바에서 스레드를 달성하기 위해 두 가지 방법이 있다는 것을 알고있다 :

  • Thread 클래스 상속
  • Runnable를 구현

어느 쪽이든, 그러나, 여전히 스레드의 시작 () 메서드를 호출하여 마지막 스레드를 수행 할 필요가

중요한 속성과 Thread 클래스의 방법에서 살펴 보자 :

// 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);
复制代码

어떤이 코드가 출력에서 ​​살펴 보자 :

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");
}
复制代码

코드의 내용을 출력 불확실하고 출력 할 수있다이다 :

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

출력도 가능합니다 :

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

그러나, 위의 코드 t1.start (), t2.start ()에있는 경우 :

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

그런 다음, 출력 결정된다 :

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

() 시작 왜, 출력의 내용이 불확실하고, 실행 () 출력의 사용을 결정하는 것입니다? 이 프로세스는 스레드를 이해하기 위해 처음부터 시작해야합니다. 다음과 같이 Thread 클래스의 소스 코드의 시작 () 메소드는 다음과 같습니다

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();
复制代码

그것은 시작 () 메소드 내에서 볼 수있는 것은 실제로 방법 start0에게의 기본 ()를 호출합니다. 스레드 클래스를 초기화하는 방법 RegisterNatives를 실행하는 경우에있어서 실용적 start0 관련성 JVM_StartThread 방법이고 (), 로컬 방법을 등록하려면

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

jvm.cpp에서 다음 코드 세그먼트 :

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

여기 JVMENTRY은 다음, 그 스레드 기능이 thread_entry입니다 내 JVMStartThread 기능을 정의하는 매크로, 당신은 진정한 스레드 로컬 플랫폼 특정 기능을 만들 볼 수 있습니다 :

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);
}
复制代码

당신은 매크로 정의와 vmSymbols.hpp에서 호출 vmSymbolHandles :: runmethodname 방법 및 runmethodname를 볼 수 있습니다

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

위의 코드에서, 스레드 실행 (시작)에있어서, 제 스레드이어서 얻어진 새로운 운영 체제 스레드를 생성 할 때 운영 CPU 시간 슬라이스 수행 콜백 방법 RUN (), 이는 당신은 단지에만 일반 스레드 객체를 수행하기 위해 run () 메소드를 통해 시작 () 새 스레드 및 실행 신체의 비동기 스레드에 의해 입증 만들 수 있으며 병렬로 실행되지 않지만 직렬 실행.

다음은 당신에게 스레드와 관련된 일반적인 문제 중 일부를 보내고 있습니다 :

的 잠을 스레드에 가입, 수율

  • 1.sleep
  1. 수면은 ()하므로 CPU 사용량이 다른 스레드에 대한 약간의 시간을두고 있음, 현재 정체 상태로 스레드 (블록 현재의 thread)를 만든다
  2. 잠은 개체 잠금 수면을 해제하지 않을 때
  • 2.join A의 하나 개의 스레드에서 실행이 방법 A 스레드 B에 가입 한 후이 일시 중단됩니다, B 기다리고 후속 작업의 실행 후 종료
public static void main(String[] args){
    Thread t1 = new Thread();
    t1.start();
    t1.join();
    // 以下代码会在t1执行完毕后打印
    System.out.println("t1 finished");
}
复制代码
  • 3.yield
  1. 수율은 사람의 요구가 스레드 스케줄링을 말할 경우, 당신이 그것을 취할 수, 나도 내가 계속 다시 아무도 요구를 수행 할 것,하지 평균 종료 및 일시 정지 않지만,
  2. 잠금이 해제되지 않은 전화 수율

객체 的 대기, 통지의 notifyAll

  • 1.wait
  1. 대기 () 메소드는 클래스 객체의 메소드이다 수행 스레드를 대기 할 때 () 메소드 때
  2. 다시 깨어날 수, 락 객체를 잃고 잠금을 획득하는 동안 스레드 풀과 관련된 대기와 개체를 입력
  3. 대기 () 통지 () 또는 사용의 notifyAll () 또는 현재 풀에서 대기중인 스레드를 깨워 수면 시간을 지정
  4. 대기는 (), 그렇지 않으면 "java.lang.IllegalMonitorStateException을"오류가 발생하지 동기화 블록에 배치해야합니다
  • 2.notify

는 "대상 모니터"로 작동해야합니다) ((대기) 및 통지

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();
}
复制代码

스레드와 차이의 Runnable

  • 스레드 대상 속성이 실행 가능한 상태에서 스레드의 시작에 의해 실행 가능한, 시작 (실제로는 목표의 실행 방법이라고도 함)
  • 실행 가능한 속성은 자원 공유를 달성 할 수있다, 스레드가 자원을 공유 할 수 없습니다
MyRunnable r = new MyRunnable();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
// t1/t2线程操作的都是同一个实例r,所以r中的数据可以实现多线程共享
t1.start();
t2.start();
复制代码

스레드간에 통신하는 방법

  • 가입 : 다른 스레드에 대한 스레드 대기가 실행 전에 완료
  • 대기 / (가) 통보 : 다른 스레드에 대한 스레드 대기는 실행 후 객체의 모니터를 소유하고 나면
  • CountDownLatch를 : 대기하는 스레드 (countDownLatch.await ()) 다른 스레드의 실행은 임의의 수 (countDownLatch.countDown ())을 실행 한 후에 완료
  • 으로 CyclicBarrier : 자신의 준비 전에 모든 스레드는, 통일은 모든 스레드가 준비가되었을 때 후속 시작했다 (모두) (cyclicBarrier.await 호출)
  • 세마포어 : 당신은 동시에 스레드 액세스 수를 제어 할 수 있습니다 기다릴 경우, 취득 ()에 의해 라이센스를 취득하고, 릴리스 () 허가를 해제
  • 호출 가능는 : 하위 스레드 실행 결과는 부모 스레드에 백업
FutureTask<Integer> futureTask = new FutureTask<>(callable);
new Thread(futureTask).start();
Object result = futureTask.get();
复制代码
  1. CountDownLatch를하고 스레드 간의으로 CyclicBarrier 대기를 달성 할 수 있지만 서로 다른 강조점을 가지고 :
  2. CountDownLatch를 A는 일반적으로 작업을 실행하는 여러 가지 다른 스레드를 대기하는 스레드에 사용 후에는 실행되었다;
  3. 일반적으로 CyclicBarrier는 특정 상태로 서로 기다렸다가 동시에 그 스레드 그룹을 실행 스레드 그룹에 사용;
  4. 또한, CountDownLatch를 재사용 할 수 없습니다, 그리고으로 CyclicBarrier는 재사용 할 수 있습니다.
  5. 사실, 세마포 및 잠금 다소 유사은, 일반적으로 특정 리소스 세트에 대한 액세스를 제어하는 ​​데 사용됩니다.

원리 스레드 풀

스레드 풀은 두 개의 매개 변수가 있습니다 커널 스레드의 수와 스레드의 최대 수 coreNum maxNum

어떠한 스레드가없는 경우에, 초기화 스레드 코어 수가 5 스레드의 최대 수는 10이다은 스레드 풀을 초기화 스레드 풀을 가정

작업 후 왔을 때, 당신은 일을 다시하고 스레드를 초기화하면 처음 여섯 개 작업을 오는 경우, 연속 스레드가 5 초기화 스레드를 초기화

그런 다음 블록 큐에 여섯 번째 임무는 것이다

이제 스레드 풀에서 다섯 개 스레드가 하나 개의 스레드가 유휴 상태 일 경우, 그것은 실행을 위해 큐를 차단에서 처음 여섯 개 작업을 얻을 것이다

다섯 개 스레드 풀 스레드 상태를 실행하는 경우 첫 번째 작업은 블록 큐에 저장된다

큐가 가득, 우리는 스레드의 최대 수는 10이지만, 풀에 다섯 스레드 설정하면 다음 작업 큐에 저장할 수 없습니다 여섯 개 스레드 풀 스레드 이번에 차단을 실행하기 위해 새로운 스레드를 생성

풀의 스레드 수 (10), 그리고에 도달하고 또한 큐가 가득 차단하면 기능을 거부 정의하여 이러한 작업에 갈 수

실행이 끝날 때 작업 큐를 차단 시간 동안 마지막 실행, 후, 스레드 풀의 스레드 수는 코어 스레드가 자동으로 유휴 시간 이내에 복구 초과

추천

출처juejin.im/post/5e144acaf265da5d381d114f