多線程

1.線程相關的概念html

1.程序(program):是一個靜態的狀態,通常存儲在硬盤中
2.進程(process):一個正在運行的程序是一個動態的概念,通常存儲在內存中。 
3.線程(thread):一條獨立的執行路徑。多線程,在執行某個程序的時候,該程序能夠有多個子任務,每一個線程均可以獨立的完成其中一個任務。在各個子任務之間,沒有什麼依賴關係,能夠單獨執行。

2.進程和線程的關係java

一個進程中至少有一個線程,能夠有多個線程,一個進程中的全部線程,共享同一個進程中的資源

3.並行和併發數據庫

並行(parallel):多個任務(進程、線程)同時運行。在某個肯定的時刻,有多個任務在執行,條件:有多個cpu,多核編程
併發(concurrent):多個任務(進程、線程)同時發起。不能同時執行的(只有一個cpu),只能是同時要求執行。就只能在某個時間片內,將多個任務都有過執行。一個cpu在不一樣的任務之間,來回切換,只不過每一個任務耗費的時間比較短,cpu的切換速度比較快,因此可讓用戶感受就像多個任務在同時執行。(實際開發中的多線程程序,都是併發運行機制)

4.多線程的實現方式編程

第一種:繼承Thread類緩存

1) 定義一個類,繼承Thread類
2) 重寫自定義類中的run方法,用於定義新線程要運行的內容
3) 建立自定義類型的對象
4) 調用線程啓動的方法:start方法

代碼實現安全

//自定義類
package com.tohka.pojo;

public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 1;i<=10;i++){
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}
//測試類
package com.tohka.junit;

import com.tohka.pojo.MyThread;

public class Junit1 {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();


        for (int i = 1;i<=10;i++){
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}

//使用匿名內部類的方式來實現
package com.tohka.junit;

import com.tohka.pojo.MyThread;

public class Junit2 {
    public static void main(String[] args) {
        new MyThread(){
            @Override
            public void run() {
                for (int i = 1;i<=10;i++){
                    System.out.println(Thread.currentThread().getName()+i);
                }
            }
        }.start();


        for (int i = 1;i<=10;i++){
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}

第二種:實現實現Runnable接口多線程

1)定義一個任務類,實現Runnable接口
2)重寫任務類中的run方法,用於定義任務的內容
3)建立任務類對象,表示任務
4)建立一個Thread類型的對象,將任務對象做爲構造參數傳遞,用於執行任務類對象
   Thread(Runnable able);
5)調用線程對象的start方法,開啓新線程
   調用的就是Thread類構造方法中傳遞的able線程任務中的run方法

代碼實現:併發

//任務類
package com.tohka.pojo;

public class TaskThread implements Runnable {
    public void run() {
        for (int i = 1;i<=10;i++){
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}
//測試類
package com.tohka.junit;

import com.tohka.pojo.TaskThread;

public class Junit3 {
    public static void main(String[] args) {
        TaskThread ts = new TaskThread();

        Thread thread = new Thread(ts);
        thread.start();

        for (int i = 1;i<=10;i++){
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}
//使用匿名內部類實現

package com.tohka.junit;

import com.tohka.pojo.TaskThread;

public class Junit3 {
    public static void main(String[] args) {
       new Thread(new TaskThread(){
           @Override
           public void run() {
               for (int i = 1;i<=10;i++){
                   System.out.println(Thread.currentThread().getName()+i);
               }
           }
       }).start();

        for (int i = 1;i<=10;i++){
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}

第三種:實現Callable接口異步

實現思路:ide

若是建立Thread對象,執行Runnable任務,須要Runnable對象

Runnable是一個接口,有一個特殊的實現類

FutureTask是Runnable的一個實現類

FutureTask在建立對象的時候,須要傳遞一個Callable的對象

Callable是一個接口,因此咱們須要一個Callable的實現類

1)定義一個自定義類實現Callable接口
2)在自定義類中重寫call()方法
3)建立自定義類的對象
4)建立Future的實現類FutureTask對象,把自定義類的對象做爲構造方法的參數
5)建立Thread類的對象,把FutureTask對象做爲構造方法的參數
6)使用Thread類的對象調用start方法啓動線程
7)FutureTask對象用get方法,就能夠獲取線程結束以後的結果。

代碼實現

//自定義類
package com.tohka.pojo;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<Integer> {
    public Integer call() throws Exception {
        int i ;
        for (i = 1;i<=10;i++){
            System.out.println(Thread.currentThread().getName()+i);
        }

        return i;
    }
}

//測試類
package com.tohka.junit;

import com.tohka.pojo.MyCallable;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Junit4 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable callable = new MyCallable();
        FutureTask<Integer> futureTask = new FutureTask<Integer>(callable);
        Thread thread = new Thread(futureTask);
        thread.start();
        Integer i = futureTask.get();
        System.out.println(thread.currentThread().getName()+i);
    }
}

第四種:經過線程池實現

1.建立一個指定線程數量的線程池 Executors.newFixedThreadPool(int nThreads)
2.建立任務類對象:Runnable的實現類對象,用於定義任務內容
3.將一個任務類對象,提交到線程池中,若是有空閒的線程,就能夠立刻運行這個任務,若是沒有空閒線程,那麼這個任務就須要等待  submit(Runnable r)
4.結束線程池 shutDown()、shutDownNow()

代碼實現

package com.tohka.pool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo1 {
    public static void main(String[] args) {
        ExecutorService ex = Executors.newFixedThreadPool(3);
        Runnable r1 = new Runnable() {
            public void run() {
                for (int i = 1;i<=10;i++){
                    System.out.println(Thread.currentThread().getName()+" "+i);
                }
            }
        };
        Runnable r2 = new Runnable() {
            public void run() {
                for (int i = 1;i<=10;i++){
                    System.out.println(Thread.currentThread().getName()+" "+i);
                }
            }
        };
        Runnable r3 = new Runnable() {
            public void run() {
                for (int i = 1;i<=10;i++){
                    System.out.println(Thread.currentThread().getName()+" "+i);
                }
            }
        };
        ex.submit(r1);
        ex.submit(r2);
        ex.submit(r3);
        ex.shutdown();
    }
}

5.線程優先級

線程默認優先級是5;線程優先級的範圍是:1-10
Thread類提供的優先級常量(靜態常量)
        MIN_PRIORITY = 1;		最低
        NORM_PRIORITY = 5;		默認
        MAX_PRIORITY = 10;		最高

注意 : 哪怕給線程設定了優先級,沒有辦法保證,哪些線程必定最早運行; 線程優先級別高的, 獲取CPU資源的機率大, 但線程的執行仍然具備很大的隨機性

6.線程禮讓

static void yield()
線程讓步,暫停當前正在執行的線程,把執行機會讓給優先級相同或更高的線程若隊列中沒有同優先級的線程,忽略此方法

測試代碼

package com.tohka.junit;

public class Junit5 {
    public static void main(String[] args) {
        Thread thread1 = new Thread("禮讓線程"){
            @Override
            public void run() {
                System.out.println(getName());
            }
        };
        thread1.yield();
        thread1.setPriority(Thread.MAX_PRIORITY);

        Thread thread2 = new Thread("不讓線程1"){
            @Override
            public void run() {
                System.out.println(getName());
            }
        };

        thread2.setPriority(Thread.MAX_PRIORITY);

        Thread thread3 = new Thread("不讓線程2"){
            @Override
            public void run() {
                System.out.println(getName());
            }
        };

        thread3.setPriority(Thread.MAX_PRIORITY);
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

執行結果:

不讓線程1
禮讓線程
不讓線程2

出現問題緣由:

沒法100%禮讓,只能說禮讓會盡可能後執行

7.線程中斷

中斷這個線程休眠,等待狀態,只中斷一次。

想中斷那個線程,必須到另外一個線程中取中斷,由於本身不能中斷本身

代碼案例

package com.tohka.junit;

public class Junit5 {
    public static void main(String[] args) {
        Thread t3 = new Thread("不讓線程2"){
            @Override
            public void run(){
                for(int i = 1; i <= 10; i++){
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(getName() + "--" +i);
                }
            }
        };

        t3.start();
        t3.interrupt();
    }
}

8.守護線程

若是一個程序的運行中,沒有非守護線程了,只有守護線程了,那麼守護線程會過一段時間後自動中止

final void setDaemon(boolean on) : 參數設置爲true,將線程設置爲守護線程

​ 將此線程標記爲daemon線程或用戶線程。當運行的惟一線程都是守護進程線程時,Java虛擬機將退出

package com.tohka.junit;

public class Junit5 {
    public static void main(String[] args) {
        Thread thread = new Thread("守護線程") {
            @Override
            public void run() {
                for(int i = 1; true; i++){
                    System.out.println(getName() + "--" +i);
                }
            }
        };

        thread.setDaemon(true);

        Thread thread1 = new Thread("普通線程") {
            @Override
            public void run(){
                for(int i = 1; i <= 10; i++){
                    System.out.println(getName() + "--" +i);
                }
            }
        };

        thread.start();
        thread1.start();
    }
}

9.使用同步代碼塊保證線程安全的原理

當cpu想去執行同步代碼塊的時候,須要先獲取到鎖對象,獲取以後就能夠運行代碼塊中的內容;當cpu正在執行當前代碼塊的內容時,cpu能夠切換到其餘線程,可是不能切換到須要相同鎖對象的線程上。
 	當cpu執行完當前代碼塊中的代碼以後,就會釋放鎖對象,cpu就能夠運行其餘須要當前鎖對象的同步代碼塊了

10.同步方法的鎖對象

若是是非靜態的方法,同步方法的鎖對象就是this,當前對象,哪一個對象調用這個同步方法,這個同步方法使用的鎖就是哪一個對象
 	若是是靜態的方法,同步方法的鎖對象就是當前類的字節碼對象,類名.class(在方法區的一個對象),哪一個類在調用這個同步方法,這個同步方法使用的鎖就是哪一個類的字節碼對象

11.線程的生命週期

新建態:剛建立好對象的時候,剛new出來的時候
 	就緒態:線程準備好了全部運行的資源,只差cpu來臨
 	運行態:cpu正在執行的線程的狀態
 	阻塞態:線程主動休息、或者缺乏一些運行的資源,即便cpu來臨,也沒法運行
 	死亡態:線程運行完成、出現異常、調用方法結束

**12.wait()與 notify()方法 **

wait() 讓線程等待
notify()喚醒等待的線程
notify()和 notifyAll()
通常來講,全部等待的線程 會按照順序進行排列,若是如今使用了 notify()方法的話,則會喚醒第一個等 待的線程執行,而若是使用了 notifyAll()方法,則會喚醒全部的等待線程,那 個線程的優先級高,那個線程就有可能先執行。

13.sleep() 和 wait()的區別

一、這兩個方法來自不一樣的類分別是,sleep來自Thread類,wait來自Object類。

二、最主要是sleep方法沒有釋放鎖,而wait方法釋放了鎖,使得其餘線程可使用同步控制塊或者方法。

sleep不出讓系統資源;wait是進入線程等待池等待,出讓系統資源,其餘線程能夠佔用CPU。通常wait不會加時間限制,由於若是wait線程的運行資源不夠,再出來也沒用,要等待其餘線程調用notify/notifyAll喚醒等待池中的全部線程,纔會進入就緒隊列等待OS分配系統資源。sleep(milliseconds)能夠用時間指定使它自動喚醒過來,若是時間不到只能調用interrupt()強行打斷。

Thread.Sleep(0)的做用是「觸發操做系統馬上從新進行一次CPU競爭」。

三、使用範圍:wait,notify和notifyAll只能在同步控制方法或者同步控制塊裏面使用,而sleep能夠在任何地方使用 
   synchronized(x){ 
      x.notify() 
     //或者wait() 
   }

四、sleep必須捕獲異常,而wait,notify和notifyAll不須要捕獲異常

14.線程同步的幾種方式

一、synchronized  能夠用來同步方法和同步代碼塊
   同步方法:
   	  給一個方法增長synchronized關鍵字,能夠是靜態方法(鎖住整個類),也能夠是非靜態方法(不能是抽象方法)
   同步代碼塊:經過鎖定一個指定的對象,來對同步代碼塊進行同步
   同步是高開銷操做,儘可能少使用同步方法,同步關鍵代碼的代碼塊便可
二、Lock鎖
ReentrantLock 代碼經過lock()方法獲取鎖,但必須調用unlock()方法釋放鎖

15.如何理解線程池思想?Java中的線程池有哪些?

建立線程每次都要和操做系統進行交互,線程執行完任務後就會銷燬,若是頻繁的大量去建立線程確定會形成系統資源開銷很大,下降程序的運行效率。

線程池思想就很好的解決了頻繁建立線程的問題,咱們能夠預先建立好一些線程,把他們放在一個容器中,須要線程執行任務的時候,從容器中取出線程,任務執行完畢後將線程在放回容器。
一:newCachedThreadPool(緩存線程型池)
二:newFixedThreadPool  (緩存線程池和上面的差很少)
三:ScheduledThreadPool(調度型線程池)
四:SingleThreadExecutor(單例線程)
詳細解析網址:https://zhidao.baidu.com/question/1116515319242598059.html

16.開發中哪些地方用到了多線程?

你能夠說咱們的作的項目幾乎沒有用到多線程 ,
涉及到的地方几乎都用 搭建集羣的方式來解決

17.什麼是線程同步、異步?

1.線程同步:是多個線程同時訪問同一資源,等待資源訪問結束,浪費時間,效率不高
     
2.線程異步:訪問資源時,若是有空閒時間,則可在空閒等待同時訪問其餘資源,實現多線程機制

18.說一下 Runnable和 Callable有什麼區別?

1)Runnable接口中的方法沒有返回值;Callable接口中的方法有返回值

2)Runnable接口中的方法沒有拋出異常;Callable接口中的方法拋出了異常

3)Runnable接口中的落地方法是call方法;Callable接口中的落地方法是run方法

詳細連接:https://blog.csdn.net/syc0616/article/details/109734464

19.線程的run()和start()的區別

1.調用 start() 方法是用來啓動線程的,輪到該線程執行時,會自動調用 run();直接調用 run() 方法,沒法達到啓動多線程的目的,至關於主線程線性執行 Thread 對象的 run() 方法。
2.一個線程對線的 start() 方法只能調用一次,屢次調用會拋出 java.lang.IllegalThreadStateException 異常;run() 方法沒有限制。

20.synchronized和lock的區別

做用位置不一樣:
synchronized能夠給方法、代碼塊加鎖
lock只能給代碼塊加鎖
鎖的獲取和釋放機制不一樣:
synchronized無需手動獲取鎖和釋放鎖,發生異常會自動解鎖,不會出現死鎖
   修飾成員方法時,默認的鎖對象,就是當前對象
   修飾靜態方法時,默認的鎖對象,是當前類的Class對象  好比:Person.class
   修飾代碼塊時,能夠本身來設置鎖對象,好比:
   synchronized(this){
     //線程進入,自動獲取到鎖
     //線程執行結束,自動釋放鎖
   }

lock須要本身加鎖和釋放鎖,如lock()和unlock(),若是忘記使用unlock(),則會出現死鎖,因此通常會在finally裏使用unlock()

21.樂觀鎖和悲觀鎖的區別

1.悲觀鎖假定會發生衝突,訪問的時候都要先得到鎖,保證同一個時刻只有線程得到鎖,讀讀也會阻塞
	就是在操做數據時,認爲此操做會出現數據衝突,因此在進行每次操做時都要經過獲取鎖才能進行對相同數據的操做,這點跟java中的synchronized很類似,因此悲觀鎖須要耗費較多的時間。另外與樂觀鎖相對應的,悲觀鎖是由數據庫本身實現了的,要用的時候,咱們直接調用數據庫的相關語句就能夠了
二、樂觀鎖假定不會發生衝突,只有在提交操做的時候檢查是否有衝突
   主要適用場景:當要更新一條記錄的時候,但願這條記錄沒有被別人更新,也就是說實現線程安全的數據更新
實現方式:
 (1)取出記錄時,獲取當前version
 (2)更新時,帶上這個version
 (3)執行更新時, set version = newVersion where version = oldVersion
 (4)若是version不對,就更新失敗
詳情網址:https://www.cnblogs.com/qlqwjy/p/7798266.html

22.多線程怎麼解決高併發?

1.synchronized 關鍵字主要解決多線程共享數據同步問題。 
2.ThreadLocal 使用場合主要解決多線程中數據因併發產生不一致問題。 
3.ThreadLocal 和 Synchonized 都用於解決多線程併發訪問可是 ThreadLocal 與 synchronized 有本質的區別:          (
	(1)synchronized 是利用鎖的機制,使變量或代碼塊在某一時該只能被一個線程訪問。
   (2) ThreadLocal 是爲每個線程都提供了變量的副本,使得每一個線程在某一時間訪問到的並 不是同一個對象,這樣就隔離了多個線程對數據的數據共享。而 Synchronized 卻正好相反, 它用於在多個線程間通訊時可以得到數據共享。

23.Java 關鍵字 volatile 與 synchronized 做用與區別?

1.volatile
它所修飾的變量不保留拷貝,直接訪問主內存中的。
在Java內存模型中,有main memory,每一個線程也有本身的memory (例如寄存器)。爲了性能,一個線程會在本身的memory中保持要訪問的變量的副本。這樣就會出現同一個變 量在某個瞬間,在一個線程的memory中的值可能與另一個線程memory中的值,或者main memory中的值不一致的狀況。 一個變量聲明爲volatile,就意味着這個變量是隨時會被其餘線程修改的,所以不能將它cache在線程memory中。
2.synchronized
當它用來修飾一個方法或者一個代碼塊的時候,可以保證在同一時刻最多隻有一個線程執行該段代碼。
區別:
1、volatile是變量修飾符,而synchronized則做用於一段代碼或方法。
2、volatile只是在線程內存和「主」內存間同步某個變量的值;而synchronized經過鎖定和解鎖某個監視器同步全部變量的值。顯然synchronized要比volatile消耗更多資源。

24.什麼是 Java Timer 類?如何建立一個有特定時間間隔的任務?

java.util.Timer 是一個工具類.能夠用於安排一個線程在將來的某個特定時間 執行。Timer 類能夠用安排一次性任務或者週期任務。 java.util.TimerTask 是一個實現了 Runnable 接口的抽象類.咱們須要去繼承 這個類來建立咱們本身的定時任務並使用 Timer 去安排它的執行。

25.怎麼檢測一個線程是否擁有鎖?

在 java.lang.Thread 中有一個方法叫 holdsLock(),它返回 true 若是當且僅當當 前線程擁有某個具體對象的鎖

**26.Java 中的鎖有幾種方式 **

兩種: Synchronized Lock
1.Synchronized的侷限性:
   1.若是這個獲取鎖的線程因爲要等待IO或者其餘緣由(好比調用sleep方法)被阻塞了,但 是又沒有釋放鎖,其餘線程便只能乾巴巴地等待。(不能主動釋放鎖) 
   2.當有多個線程讀寫文件時,讀操做和寫操做會發生衝突現象,寫操做和寫操做會發生衝突 現象,可是讀操做和讀操做不會發生衝突現象若是多個線程都只是進行讀操做,因此當一個 線程在進行讀操做時,其餘線程只能等待沒法進行讀操做。(不分狀況,一概鎖死)
2.Lock 的幾個實現類 ReentrantLock是一個可重入的互斥鎖,又被稱爲「獨佔鎖」 ReadWriteLock,顧名思義,是讀寫鎖。它維護了一對相關的鎖 — — 「讀取鎖」和「寫入 鎖」,一個用於讀取操做,另外一個用於寫入操做。他的兩個實現類讀鎖readerLock和寫鎖 writerLock。
相關文章
相關標籤/搜索