1,什麼是線程?線程和進程的區別是什麼?
2,什麼是多線程?為什麼設計多線程?
3,Java種多線程的實現方式是什麼?有什麼區別?
4,線程的狀態控制有哪些方法?
5,線程安全、死鎖和生產者--消費者
6,線程的優化有哪些方法?
1,什麼是線程?線程和進程的區別是什麼?
線程是程序執行的最小單元。
區別: 進程是操作系統進行資源處理和分配的最小單位,而一個進程可以包含多個線程,並共享進程的資源。
2,什麼是多線程?為什麼設計多線程?
介紹之前,我們需要理解並行和並發的定義:
並行:同一個時刻有多個線程進行。
並發:同一個時間段內有多個線程進行。
多線程指的是一個進程可以包含多個並發的線程(同一個時刻只有一個線程運行)。例如酷狗,我們可以一邊聽歌一邊搜索自己喜歡的歌曲。多線程的存在能夠讓進程及時處理我們多項的請求,提高應用程序的利用率。
多線程編程需要了解到多線程運行面臨的問題。
3,Java種多線程的實現方式是什麼?有什麼區別?
Java實現多線程有兩個方式。
線程類MyThread:
public class MyThread extends Thread{ @Override public void run() { for(int x=0;x<200;x++){ System.out.println(x); } } }View Code
主類Demo:
public class Demo { public static void main(String[] args) { MyThread mt = new MyThread();//新建線程類對象 MyThread mt1 = new MyThread(); mt.start();//調用start()方法 mt1.start(); } }View Code
ps,為什麼不直接調用線程類的run()方法,而調用start()方法?
run()方法只是封裝了多線程執行的操作,只是一個普通方法。
start()方法是啟動線程執行的方法,由JVM自動調用run()方法。
步驟:1,編寫實現Runnable 接口的類,重寫run()方法
public class ThreadRunnable implements Runnable { public void run(){}}
2,在主類中新建線程類對象,obj
ThreadRunnable tr = new ThreadRunnable();
3,新建Thread類t,將obj作為t的構造參數
Thread t = new Thread(tr);
4,調用t的start()方法。
由於Java只允許單類繼承,故多選用實現Runnable接口的方法創建多線程,事實上Thread類也是接口Runnable的實現類。
public class Thread extends Object implements Runnable
4,線程的狀態控制有哪些方法?
線程狀態控制常用到的方法如下:
t.sleep(1000);讓線程t睡眠1000毫秒,即1秒。
A.start();
A.join();//try catch
B.start();
C.start();
A執行完之後B和C才可以執行
A.yield();A暫停一下,時間不確定,讓同等級的線程優先運行。
A.interrupt();把線程的狀態中止,並拋出 InterruptedException 。跳出阻塞的部分可以繼續執行接下來的代碼。
interrupt()只是改變中斷狀態而已. interrupt()不會中斷一個正在運行的線程。這一方法實際上完成的是,給受阻塞的線程拋出一個中斷信號,這樣受阻線程就得以退出阻塞的狀態。更確切 的說,如果線程被Object.wait, Thread.join和Thread.sleep三種方法之一阻塞, 它將接收到一個中斷異常(InterruptedException),從而提早地終結被阻塞狀態。
如果線程沒有被阻塞,這時調用interrupt()將不起作用;否則,線程就將得到InterruptedException異常(該線程必須事先預備好處理此狀況),接著逃離阻塞狀態。
ps:對於一個正在運行的線程如果想要其結束運行,可以使用標志位,讓線程跳出從而結束運行,示例如下:
public class ThreadFlag extends Thread { public volatile boolean exit = false; public void run() { while (!exit); } }View Code
5,線程安全、死鎖和生產者--消費者
我們來看下面的一段代碼:
public class ThreadRunnable implements Runnable { private static int D = 100;//D是靜態變量由多個線程共享。public void run() { while(true){if(D>0){ try { Thread.currentThread().sleep(100); } catch (InterruptedException e1) { e1.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"---"+(D--)); } } }//run }View Code
由於D是靜態變量,它由ThreadRunnable的所有對象訪問,每個對象對它進行輸出,並減1的操作,直到D為0(可將D假想成某直達列車的票,每 個ThreadRunnable對象是一個售票窗口)假設有三個窗口如下:
public class ThreadRunnableDemo { public static void main(String[] args) { ThreadRunnable tr = new ThreadRunnable();//創建接口對象 Thread t1 = new Thread(tr,"窗口1");//創建Thread類,將上述對象作為構造參數 Thread t2 = new Thread(tr,"窗口2");//創建Thread類,將上述對象作為構造參數 Thread t3 = new Thread(tr,"窗口3");//創建Thread類,將上述對象作為構造參數 t1.start();//啟動start方法 t2.start(); t3.start(); } }View Code
輸出:
.... 窗口1---97 窗口2---97 .... .... 窗口3---2 窗口1---1 窗口2---0 窗口3----1View Code
出同號票的原因分析:
出現0號和負號票的原因分析:
這就產生了線程不安全的問題,產生線程不安全的場景:
多個線程訪問同一資源,並對資源進行多條語句操作就有可能引發線程不安全。
概括:多個線程;同一資源;不是原子操作
前兩個條件我們無法改變,我們有的解決思路就是將線程對資源操作語句封裝成原子操作(不會被打斷)。將操作封裝成原子操作。
Java使用synchronized關鍵字。
使用規范:
public void run() { while(true){ synchronized(new Object()){ if(D>0){ try { Thread.currentThread().sleep(100); } catch (InterruptedException e1) { e1.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"---"+(D--)); } } } }//run()View Code
下面考慮更復雜的情況------死鎖
死鎖顧名思義就是加的鎖打不開,一般發生在當兩個線程互相拿著對方的鎖即鎖嵌套,造成兩個線程一直處於等待中。死鎖示例:
經典的生產者和消費者問題
問題描述:生產者producer生產資源,消費者customer消耗資源呢,我們設計的程序最低保證消費者在消耗資源時必須保證有資源。
設計思想:消費者和生產者共用一個鎖,消費者一直消費資源,直到剩余資源數小於規定(0或者業務目標),消費者線程進入等待,直到生產者生產資源後將自己喚醒。
實現:
Producer類
package producer; import java.util.ArrayList; public class Producer implements Runnable { private ArrayList<String> al; public Producer(ArrayList<String> al){ this.al = al; } @Override public void run() { while(true){ synchronized (al) { while(al.size()>0){ try { al.wait();//只要有資源,生產線程就wait() } catch (InterruptedException e) { e.printStackTrace(); } } for(int i=0;i<10;i++){ al.add(""+i); } al.notify();//生產完成,喚醒等待線程即消費者線程 } } } }View Code
Customer類
package producer; import java.util.ArrayList; public class Customer implements Runnable { private ArrayList<String> al; public Customer(ArrayList<String> al){ this.al = al; } @Override public void run() { while(true){ synchronized (al) { while(al.size()<1){ try { al.wait();//如果沒資源,消費者線程就wait() } catch (InterruptedException e) { e.printStackTrace(); } } while(al.size()>0){ System.out.println("消費者:"+al.size()); al.remove(0); } al.notify();//消費沒了,喚醒等待線程即生產者線程 } } } }View Code
調用類Demo
package producer; import java.util.ArrayList; public class Demo { private static ArrayList<String> al= new ArrayList<String>(); public static void main(String[] args) { Producer p = new Producer(al); Customer c = new Customer(al); Thread producer = new Thread(p); Thread customer = new Thread(c); producer.start(); customer.start(); } }View Code
6,線程的優化有哪些方法?
實際業務場景中線程的壽命都很短暫,例如對於網站訪問,每個用戶請求是一個線程,如果來一個用戶,進行一套線程的創建、就緒等動作會嚴重影響
服務器的響應效率,鑒於此,Java中有了線程池的解決辦法,它的思想是程序初始運行時在一個容器內新建固定數量的線程,當用到時從容器內取出一個線程,
線程執行完之後再放回到容器內,實質是以空間換時間,這個容器在Java中就被稱為線程池。
Java實現線程池
Executors類工廠類
方法:
public static ExecutorService new FixedThreadPool(int nThreads);//該方法返回一個含有n個線程的線程池接口
線程池接口:ExecutorService
方法:
submit(Runnable task);//將一個線程類加入到線程池
結束:shutdown()
ps:福利: