bl双性强迫侵犯h_国产在线观看人成激情视频_蜜芽188_被诱拐的少孩全彩啪啪漫画

java中的線程

什么是線程?

創(chuàng)新互聯(lián)專注于湄潭網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗。 熱誠為您提供湄潭營銷型網(wǎng)站建設(shè),湄潭網(wǎng)站制作、湄潭網(wǎng)頁設(shè)計、湄潭網(wǎng)站官網(wǎng)定制、微信平臺小程序開發(fā)服務(wù),打造湄潭網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供湄潭網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。

線程(英語:thread)是操作系統(tǒng)能夠進行運算調(diào)度的最小單位。它被包含在進程之中,是進程中的實際運作單位。一條線程指的是進程中一個單一順序的控制流,一個進程中可以并發(fā)多個線程,每條線程并行執(zhí)行不同的任務(wù)。在Unix System V及SunOS中也被稱為輕量進程(lightweight processes),但輕量進程更多指內(nèi)核線程(kernel thread),而把用戶線程(user thread)稱為線程。
線程是獨立調(diào)度和分派的基本單位。線程可以為操作系統(tǒng)內(nèi)核調(diào)度的內(nèi)核線程,如Win32線程;由用戶進程自行調(diào)度的用戶線程,如Linux平臺的POSIX Thread;或者由內(nèi)核與用戶進程,如Windows 7的線程,進行混合調(diào)度。

  • 繼承Thread類
  • 實現(xiàn)Runnable接口

我們知道,一個線程可以用來執(zhí)行一個任務(wù),并且該任務(wù)的執(zhí)行是異步的,并不會阻塞后面的代碼。在一個java進程中,包含main方法的類也是在一個線程中執(zhí)行的。在實際應(yīng)用中,如果需要處理一個比較耗時的操作,為了不影響程序整體的響應(yīng),通常會將這個耗時的操作封裝到一個線程中,異步的執(zhí)行。但是,線程是怎樣實現(xiàn)任務(wù)的異步執(zhí)行的呢?本文將深入了解Thread類,以期望得出線程執(zhí)行的秘密。

根據(jù)《深入理解JAVA虛擬機》中關(guān)于線程的章節(jié),我們得知,在java中一個Thread對應(yīng)著操作系統(tǒng)中的一個線程。而操作系統(tǒng)的線程是稀缺資源,不能無限制的創(chuàng)建線程,這也就是為什么要使用線程池的原因之一。

我們也知道,在java中要實現(xiàn)一個線程,有兩種方式:

但是不管是哪種方式,最后線程的執(zhí)行還是要通過調(diào)用Thread的start()方法

讓我們看一下Thread類的重要屬性和方法:

// target就是一個傳遞給Thread等待Thread執(zhí)行的Runnable對象
/* What will be run. */
private Runnable target;

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

// 類方法,該方法會在Thread類初始化時,在類的構(gòu)造器<clinit>中被調(diào)用,且只會調(diào)用一次,該方法主要的作用是注冊一些本地的方法,以便后期可以使用
/* 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);

讓我們看看下面這段代碼將輸出什么內(nèi)容:

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");
}

該代碼的輸出內(nèi)容是不確定的,可能輸出為:

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

為什么使用start(),輸出的內(nèi)容是不確定的,而使用run()輸出卻是確定的呢?這就需要從Thread的啟動過程開始了解了。 Thread類中start()方法的源代碼如下:

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();

可以看到,start()方法內(nèi)部其實調(diào)用了一個native的方法start0()。而在Thread類初始化時,執(zhí)行了一個registerNatives()的方法來注冊本地方法,其中start0方法實際關(guān)聯(lián)的是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是一個宏,用來定義JVMStartThread函數(shù),可以看到函數(shù)內(nèi)創(chuàng)建了真正的平臺相關(guān)的本地線程,其線程函數(shù)是thread_entry,如下:

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(),    //調(diào)用了run_method_name
    vmSymbolHandles::void_method_signature(),THREAD);
}

可以看到調(diào)用了vmSymbolHandles::runmethodname方法,而runmethodname是在vmSymbols.hpp 用宏定義的:

class vmSymbolHandles: AllStatic {
    ...
    // 這里決定了調(diào)用的方法名稱是 “run”
    template(run_method_name,"run")
    ...
}

從以上的代碼中可以看出,Thread執(zhí)行start()方法,首先會創(chuàng)建一個新的操作系統(tǒng)的線程,然后當(dāng)該操作線程得到CPU時間片時,會執(zhí)行一個回調(diào)方法:run(),這也就證明了通過start()可以創(chuàng)建一個新線程并異步執(zhí)行線程體,而通過run()只是執(zhí)行了一個Thread對象的普通方法而已,并不會并行執(zhí)行,而是串行執(zhí)行的。

以下附上一些Thread相關(guān)的常見性的問題:

Thread的sleep、join、yield

  • 1.sleep
  1. sleep()使當(dāng)前線程進入停滯狀態(tài)(阻塞當(dāng)前線程),讓出CPU的使用,以留一定時間給其他線程執(zhí)行
  2. sleep休眠時不會釋放對象的鎖
  • 2.join
    在一個線程A中執(zhí)行了線程B的join方法,則A會掛起,等待B執(zhí)行完畢后再執(zhí)行后續(xù)任務(wù)
public static void main(String[] args){
    Thread t1 = new Thread();
    t1.start();
    t1.join();
    // 以下代碼會在t1執(zhí)行完畢后打印
    System.out.println("t1 finished");
}
  • 3.yield
  1. yield并不意味著退出和暫停,只是,告訴線程調(diào)度如果有人需要,可以先拿去,我過會再執(zhí)行,沒人需要,我繼續(xù)執(zhí)行
  2. 調(diào)用yield的時候鎖并沒有被釋放

object的wait、notify、notifyAll

  • 1.wait
  1. wait()方法是Object類里的方法;當(dāng)一個線程執(zhí)行到wait()方法時
  2. 該線程就進入到一個和該對象相關(guān)的等待池中,同時失去了對象的鎖,被喚醒時再次獲得鎖
  3. wait()使用notify()或者notifyAll()或者指定睡眠時間來喚醒當(dāng)前等待池中的線程
  4. wait()必須放在synchronized塊中,否則會報錯"java.lang.IllegalMonitorStateException"
  • 2.notify

wait()和notify()必須操作同一個"對象監(jiān)視器"

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和Runnable的區(qū)別

  • Runnable可以通過Thread的start(實際調(diào)用了target的run方法)啟動,而Thread中target的屬性就是一個Runnable
  • Runnable可以實現(xiàn)屬性資源共享,Thread不可以實現(xiàn)資源共享
MyRunnable r = new MyRunnable();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
// t1/t2線程操作的都是同一個實例r,所以r中的數(shù)據(jù)可以實現(xiàn)多線程共享
t1.start();
t2.start();

線程之間如何進行通信

  • join: 一個線程等待另一個線程執(zhí)行完畢后再執(zhí)行
  • wait/notify: 一個線程等待另一個線程喚醒自己所擁有的對象監(jiān)視器后再執(zhí)行
  • CountdownLatch: 一個線程等待(countDownLatch.await())其他任意個數(shù)的線程執(zhí)行完畢后(countDownLatch.countDown())再執(zhí)行
  • CyclicBarrier: 所有線程先各自準(zhǔn)備,當(dāng)所有線程都準(zhǔn)備完畢(全部都調(diào)用了cyclicBarrier.await())后統(tǒng)一開始執(zhí)行后續(xù)操作
  • Semaphore: 可以控制同時訪問的線程個數(shù),通過acquire()獲取一個許可,如果沒有就等待,而release()釋放一個許可
  • Callable: 子線程將執(zhí)行結(jié)果返回給父線程
FutureTask<Integer> futureTask = new FutureTask<>(callable);
new Thread(futureTask).start();
Object result = futureTask.get();

1) CountDownLatch和CyclicBarrier都能夠?qū)崿F(xiàn)線程之間的等待,只不過它們側(cè)重點不同:
2) CountDownLatch一般用于某個線程A等待若干個其他線程執(zhí)行完任務(wù)之后,它才執(zhí)行;
3) CyclicBarrier一般用于一組線程互相等待至某個狀態(tài),然后這一組線程再同時執(zhí)行;
4) 另外,CountDownLatch是不能夠重用的,而CyclicBarrier是可以重用的。
5) Semaphore其實和鎖有點類似,它一般用于控制對某組資源的訪問權(quán)限。

線程池的原理

線程池有兩個參數(shù):核心線程數(shù)coreNum和最大線程數(shù)maxNum

假設(shè)初始化一個線程池,核心線程數(shù)是5,最大線程數(shù)是10,線程池初始化的時候,里面是沒有線程的

當(dāng)來了一個任務(wù)時,就初始化了一個線程,如果再來一個任務(wù),再初始化了一個線程,連續(xù)初始化了5個線程之后,如果第6個任務(wù)過來了

這時會把第6個任務(wù)放到阻塞隊列中

現(xiàn)在線程池中有了5個線程,如果其中一個線程空閑了,就會從阻塞隊列中獲取第6個任務(wù),進行執(zhí)行

如果線程池的5個線程都在running狀態(tài),那么任務(wù)就先保存在阻塞隊列中

如果隊列滿了,并且我們設(shè)置了最大線程數(shù)是10,但線程池中只有5個線程,這時會新建一個線程去執(zhí)行不能保存到阻塞隊列的任務(wù),此時線程池中有了6個線程

如果線程池中的線程數(shù)達到10個了,并且阻塞隊列也滿了,則可以通過自定義的reject函數(shù)去處理這些任務(wù)

最后運行一段時間之后,阻塞隊列中的任務(wù)也執(zhí)行完了,線程池中超過核心線程數(shù)的線程會在空閑一段時間內(nèi)自動回收

當(dāng)前名稱:java中的線程
網(wǎng)頁網(wǎng)址:http://vcdvsql.cn/article28/jhjejp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站策劃服務(wù)器托管網(wǎng)站營銷全網(wǎng)營銷推廣建站公司品牌網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站托管運營