JDK5以後的LOCK鎖的概述和使用
java
package cn.itcast_01; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class SellTicket implements Runnable { // 定義票 private int tickets = 100; // 定義鎖對象 private Lock lock = new ReentrantLock(); @Override public void run() { while (true) { try { // 加鎖 lock.lock(); if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票"); } } finally { // 釋放鎖 lock.unlock(); } } } }
package cn.itcast_01; /* * 雖然咱們能夠理解同步代碼塊和同步方法的鎖對象問題,可是咱們並無直接看到在哪裏加上了鎖,在哪裏釋放了鎖, * 爲了更清晰的表達如何加鎖和釋放鎖,JDK5之後提供了一個新的鎖對象Lock。 * * Lock: * void lock(): 獲取鎖。 * void unlock():釋放鎖。 * ReentrantLock是Lock的實現類. */ public class SellTicketDemo { public static void main(String[] args) { // 建立資源對象 SellTicket st = new SellTicket(); // 建立三個窗口 Thread t1 = new Thread(st, "窗口1"); Thread t2 = new Thread(st, "窗口2"); Thread t3 = new Thread(st, "窗口3"); // 啓動線程 t1.start(); t2.start(); t3.start(); } }
生產者消費者題代碼2並解決線程安全問題
面試
package cn.itcast_04; public class Student { String name; int age; }
package cn.itcast_04; public class SetThread implements Runnable { private Student s; private int x = 0; public SetThread(Student s) { this.s = s; } @Override public void run() { while (true) { synchronized (s) { if (x % 2 == 0) { s.name = "林青霞";//剛走到這裏,就被別人搶到了執行權 s.age = 27; } else { s.name = "劉意"; //剛走到這裏,就被別人搶到了執行權 s.age = 30; } x++; } } } }
package cn.itcast_04; public class GetThread implements Runnable { private Student s; public GetThread(Student s) { this.s = s; } @Override public void run() { while (true) { synchronized (s) { System.out.println(s.name + "---" + s.age); } } } }
package cn.itcast_04; /* * 分析: * 資源類:Student * 設置學生數據:SetThread(生產者) * 獲取學生數據:GetThread(消費者) * 測試類:StudentDemo * * 問題1:按照思路寫代碼,發現數據每次都是:null---0 * 緣由:咱們在每一個線程中都建立了新的資源,而咱們要求的時候設置和獲取線程的資源應該是同一個 * 如何實現呢? * 在外界把這個數據建立出來,經過構造方法傳遞給其餘的類。 * * 問題2:爲了數據的效果好一些,我加入了循環和判斷,給出不一樣的值,這個時候產生了新的問題 * A:同一個數據出現屢次 * B:姓名和年齡不匹配 * 緣由: * A:同一個數據出現屢次 * CPU的一點點時間片的執行權,就足夠你執行不少次。 * B:姓名和年齡不匹配 * 線程運行的隨機性 * 線程安全問題: * A:是不是多線程環境 是 * B:是否有共享數據 是 * C:是否有多條語句操做共享數據 是 * 解決方案: * 加鎖。 * 注意: * A:不一樣種類的線程都要加鎖。 * B:不一樣種類的線程加的鎖必須是同一把。 */ public class StudentDemo { public static void main(String[] args) { //建立資源 Student s = new Student(); //設置和獲取的類 SetThread st = new SetThread(s); GetThread gt = new GetThread(s); //線程類 Thread t1 = new Thread(st); Thread t2 = new Thread(gt); //啓動線程 t1.start(); t2.start(); } }
生成者消費者之等待喚醒機制思路圖
安全
生產者消費者之等待喚醒機制代碼分析
多線程
package cn.itcast_05; public class Student { String name; int age; boolean flag; // 默認狀況是沒有數據,若是是true,說明有數據 }
package cn.itcast_05; public class SetThread implements Runnable { private Student s; private int x = 0; public SetThread(Student s) { this.s = s; } @Override public void run() { while (true) { synchronized (s) { //判斷有沒有 if(s.flag){ try { s.wait(); //t1等着,釋放鎖 } catch (InterruptedException e) { e.printStackTrace(); } } if (x % 2 == 0) { s.name = "林青霞"; s.age = 27; } else { s.name = "劉意"; s.age = 30; } x++; //x=1 //修改標記 s.flag = true; //喚醒線程 s.notify(); //喚醒t2,喚醒並不表示你立馬能夠執行,必須還得搶CPU的執行權。 } //t1有,或者t2有 } } }
package cn.itcast_05; public class GetThread implements Runnable { private Student s; public GetThread(Student s) { this.s = s; } @Override public void run() { while (true) { synchronized (s) { if(!s.flag){ try { s.wait(); //t2就等待了。當即釋放鎖。未來醒過來的時候,是從這裏醒過來的時候 } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(s.name + "---" + s.age); //林青霞---27 //劉意---30 //修改標記 s.flag = false; //喚醒線程 s.notify(); //喚醒t1 } } } }
package cn.itcast_05; /* * 分析: * 資源類:Student * 設置學生數據:SetThread(生產者) * 獲取學生數據:GetThread(消費者) * 測試類:StudentDemo * * 問題1:按照思路寫代碼,發現數據每次都是:null---0 * 緣由:咱們在每一個線程中都建立了新的資源,而咱們要求的時候設置和獲取線程的資源應該是同一個 * 如何實現呢? * 在外界把這個數據建立出來,經過構造方法傳遞給其餘的類。 * * 問題2:爲了數據的效果好一些,我加入了循環和判斷,給出不一樣的值,這個時候產生了新的問題 * A:同一個數據出現屢次 * B:姓名和年齡不匹配 * 緣由: * A:同一個數據出現屢次 * CPU的一點點時間片的執行權,就足夠你執行不少次。 * B:姓名和年齡不匹配 * 線程運行的隨機性 * 線程安全問題: * A:是不是多線程環境 是 * B:是否有共享數據 是 * C:是否有多條語句操做共享數據 是 * 解決方案: * 加鎖。 * 注意: * A:不一樣種類的線程都要加鎖。 * B:不一樣種類的線程加的鎖必須是同一把。 * * 問題3:雖然數據安全了,可是呢,一次一大片很差看,我就想依次的一次一個輸出。 * 如何實現呢? * 經過Java提供的等待喚醒機制解決。 * * 等待喚醒: * Object類中提供了三個方法: * wait():等待 * notify():喚醒單個線程 //理解是解開當前的鎖,由於只執行當前的進程,即喚醒了全部線程 * notifyAll():喚醒全部線程 * 爲何這些方法不定義在Thread類中呢? * 這些方法的調用必須經過鎖對象調用,而咱們剛纔使用的鎖對象是任意鎖對象。 * 因此,這些方法必須定義在Object類中。 */ public class StudentDemo { public static void main(String[] args) { //建立資源 Student s = new Student(); //設置和獲取的類 SetThread st = new SetThread(s); GetThread gt = new GetThread(s); //線程類 Thread t1 = new Thread(st); Thread t2 = new Thread(gt); //啓動線程 t1.start(); t2.start(); } }
線程的狀態轉換圖及常見執行狀況
ide
線程組的概述和使用
測試
package cn.itcast_06; /* * 線程組: 把多個線程組合到一塊兒。 * 它能夠對一批線程進行分類管理,Java容許程序直接對線程組進行控制。 */ public class ThreadGroupDemo { public static void main(String[] args) { // method1(); // 咱們如何修改線程所在的組呢? // 建立一個線程組 // 建立其餘線程的時候,把其餘線程的組指定爲咱們本身新建線程組 method2(); // t1.start(); // t2.start(); } private static void method2() { // ThreadGroup(String name) ThreadGroup tg = new ThreadGroup("這是一個新的組"); MyRunnable my = new MyRunnable(); // Thread(ThreadGroup group, Runnable target, String name) Thread t1 = new Thread(tg, my, "林青霞"); Thread t2 = new Thread(tg, my, "劉意"); System.out.println(t1.getThreadGroup().getName()); System.out.println(t2.getThreadGroup().getName()); //經過組名稱設置後臺線程,表示該組的線程都是後臺線程 tg.setDaemon(true); } private static void method1() { MyRunnable my = new MyRunnable(); Thread t1 = new Thread(my, "林青霞"); Thread t2 = new Thread(my, "劉意"); // 我不知道他們屬於那個線程組,我想知道,怎麼辦 // 線程類裏面的方法:public final ThreadGroup getThreadGroup() ThreadGroup tg1 = t1.getThreadGroup(); ThreadGroup tg2 = t2.getThreadGroup(); // 線程組裏面的方法:public final String getName() String name1 = tg1.getName(); String name2 = tg2.getName(); System.out.println(name1); System.out.println(name2); // 經過結果咱們知道了:線程默認狀況下屬於main線程組 // 經過下面的測試,你應該可以看到,默任狀況下,全部的線程都屬於同一個組 System.out.println(Thread.currentThread().getThreadGroup().getName()); } }
生成者消費者代碼優化
優化
package cn.itcast_07; public class Student { private String name; private int age; private boolean flag; // 默認狀況是沒有數據,若是是true,說明有數據 public synchronized void set(String name, int age) { // 若是有數據,就等待 if (this.flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 設置數據 this.name = name; this.age = age; // 修改標記 this.flag = true; this.notify(); } public synchronized void get() { // 若是沒有數據,就等待 if (!this.flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 獲取數據 System.out.println(this.name + "---" + this.age); // 修改標記 this.flag = false; this.notify(); } }
package cn.itcast_07; public class SetThread implements Runnable { private Student s; private int x = 0; public SetThread(Student s) { this.s = s; } @Override public void run() { while (true) { if (x % 2 == 0) { s.set("林青霞", 27); } else { s.set("劉意", 30); } x++; } } }
package cn.itcast_07; public class GetThread implements Runnable { private Student s; public GetThread(Student s) { this.s = s; } @Override public void run() { while (true) { s.get(); } } }
package cn.itcast_07; /* * 分析: * 資源類:Student * 設置學生數據:SetThread(生產者) * 獲取學生數據:GetThread(消費者) * 測試類:StudentDemo * * 問題1:按照思路寫代碼,發現數據每次都是:null---0 * 緣由:咱們在每一個線程中都建立了新的資源,而咱們要求的時候設置和獲取線程的資源應該是同一個 * 如何實現呢? * 在外界把這個數據建立出來,經過構造方法傳遞給其餘的類。 * * 問題2:爲了數據的效果好一些,我加入了循環和判斷,給出不一樣的值,這個時候產生了新的問題 * A:同一個數據出現屢次 * B:姓名和年齡不匹配 * 緣由: * A:同一個數據出現屢次 * CPU的一點點時間片的執行權,就足夠你執行不少次。 * B:姓名和年齡不匹配 * 線程運行的隨機性 * 線程安全問題: * A:是不是多線程環境 是 * B:是否有共享數據 是 * C:是否有多條語句操做共享數據 是 * 解決方案: * 加鎖。 * 注意: * A:不一樣種類的線程都要加鎖。 * B:不一樣種類的線程加的鎖必須是同一把。 * * 問題3:雖然數據安全了,可是呢,一次一大片很差看,我就想依次的一次一個輸出。 * 如何實現呢? * 經過Java提供的等待喚醒機制解決。 * * 等待喚醒: * Object類中提供了三個方法: * wait():等待 * notify():喚醒單個線程 * notifyAll():喚醒全部線程 * 爲何這些方法不定義在Thread類中呢? * 這些方法的調用必須經過鎖對象調用,而咱們剛纔使用的鎖對象是任意鎖對象。 * 因此,這些方法必須定義在Object類中。 * * 最終版代碼中: * 把Student的成員變量給私有的了。 * 把設置和獲取的操做給封裝成了功能,並加了同步。 * 設置或者獲取的線程裏面只須要調用方法便可。 */ public class StudentDemo { public static void main(String[] args) { //建立資源 Student s = new Student(); //設置和獲取的類 SetThread st = new SetThread(s); GetThread gt = new GetThread(s); //線程類 Thread t1 = new Thread(st); Thread t2 = new Thread(gt); //啓動線程 t1.start(); t2.start(); } }
線程池的概述和使用
this
package cn.itcast_08; public class MyRunnable implements Runnable { @Override public void run() { for (int x = 0; x < 100; x++) { System.out.println(Thread.currentThread().getName() + ":" + x); } } }
package cn.itcast_08; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /* * 線程池的好處:線程池裏的每個線程代碼結束後,並不會死亡,而是再次回到線程池中成爲空閒狀態,等待下一個對象來使用。 * * 如何實現線程的代碼呢? * A:建立一個線程池對象,控制要建立幾個線程對象。 * public static ExecutorService newFixedThreadPool(int nThreads) * B:這種線程池的線程能夠執行: * 能夠執行Runnable對象或者Callable對象表明的線程 * 作一個類實現Runnable接口。 * C:調用以下方法便可 * Future<?> submit(Runnable task) * <T> Future<T> submit(Callable<T> task) * D:我就要結束,能夠嗎? * 能夠。 */ public class ExecutorsDemo { public static void main(String[] args) { // 建立一個線程池對象,控制要建立幾個線程對象。 // public static ExecutorService newFixedThreadPool(int nThreads) ExecutorService pool = Executors.newFixedThreadPool(2); // 能夠執行Runnable對象或者Callable對象表明的線程 pool.submit(new MyRunnable()); pool.submit(new MyRunnable()); //結束線程池 pool.shutdown(); } }
多線程方式3線程
package cn.itcast_09; import java.util.concurrent.Callable; //Callable:是帶泛型的接口。 //這裏指定的泛型實際上是call()方法的返回值類型。 public class MyCallable implements Callable { @Override public Object call() throws Exception { for (int x = 0; x < 100; x++) { System.out.println(Thread.currentThread().getName() + ":" + x); } return null; } }
package cn.itcast_09; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /* * 多線程實現的方式3: * A:建立一個線程池對象,控制要建立幾個線程對象。 * public static ExecutorService newFixedThreadPool(int nThreads) * B:這種線程池的線程能夠執行: * 能夠執行Runnable對象或者Callable對象表明的線程 * 作一個類實現Runnable接口。 * C:調用以下方法便可 * Future<?> submit(Runnable task) * <T> Future<T> submit(Callable<T> task) * D:我就要結束,能夠嗎? * 能夠。 */ public class CallableDemo { public static void main(String[] args) { //建立線程池對象 ExecutorService pool = Executors.newFixedThreadPool(2); //能夠執行Runnable對象或者Callable對象表明的線程 pool.submit(new MyCallable()); pool.submit(new MyCallable()); //結束 pool.shutdown(); } }
線程方式3的求和案例
code
package cn.itcast_10; import java.util.concurrent.Callable; /* * 線程求和案例 */ public class MyCallable implements Callable<Integer> { private int number; public MyCallable(int number) { this.number = number; } @Override public Integer call() throws Exception { int sum = 0; for (int x = 1; x <= number; x++) { sum += x; } return sum; } }
package cn.itcast_10; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /* * 多線程實現的方式3: * A:建立一個線程池對象,控制要建立幾個線程對象。 * public static ExecutorService newFixedThreadPool(int nThreads) * B:這種線程池的線程能夠執行: * 能夠執行Runnable對象或者Callable對象表明的線程 * 作一個類實現Runnable接口。 * C:調用以下方法便可 * Future<?> submit(Runnable task) * <T> Future<T> submit(Callable<T> task) * D:我就要結束,能夠嗎? * 能夠。 */ public class CallableDemo { public static void main(String[] args) throws InterruptedException, ExecutionException { // 建立線程池對象 ExecutorService pool = Executors.newFixedThreadPool(2); // 能夠執行Runnable對象或者Callable對象表明的線程 Future<Integer> f1 = pool.submit(new MyCallable(100)); Future<Integer> f2 = pool.submit(new MyCallable(200)); // V get() Integer i1 = f1.get(); Integer i2 = f2.get(); System.out.println(i1); System.out.println(i2); // 結束 pool.shutdown(); } }
匿名內部類實現多線程
package cn.itcast_11; /* * 匿名內部類的格式: * new 類名或者接口名() { * 重寫方法; * }; * 本質:是該類或者接口的子類對象。 */ public class ThreadDemo { public static void main(String[] args) { // 繼承Thread類來實現多線程 new Thread() { public void run() { for (int x = 0; x < 100; x++) { System.out.println(Thread.currentThread().getName() + ":" + x); } } }.start(); // 實現Runnable接口來實現多線程 new Thread(new Runnable() { @Override public void run() { for (int x = 0; x < 100; x++) { System.out.println(Thread.currentThread().getName() + ":" + x); } } }) { }.start(); // 更有難度的 // 面試題:若是兩個的話,執行的是方法體中的,也就是輸出的是world的部分 new Thread(new Runnable() { @Override public void run() { for (int x = 0; x < 100; x++) { System.out.println("hello" + ":" + x); } } }) { public void run() { for (int x = 0; x < 100; x++) { System.out.println("world" + ":" + x); } } }.start(); } }
定時器的概述和使用
package cn.itcast_12; import java.util.Timer; import java.util.TimerTask; /* * 定時器:可讓咱們在指定的時間作某件事情,還能夠重複的作某件事情。 * 依賴Timer和TimerTask這兩個類: * Timer:定時 * public Timer() * public void schedule(TimerTask task,long delay) * public void schedule(TimerTask task,long delay,long period) * public void cancel() * TimerTask:任務 */ public class TimerDemo { public static void main(String[] args) { // 建立定時器對象 Timer t = new Timer(); // 3秒後執行爆炸任務 // t.schedule(new MyTask(), 3000); //結束任務 t.schedule(new MyTask(t), 3000); } } // 作一個任務 class MyTask extends TimerTask { private Timer t; public MyTask(){} public MyTask(Timer t){ this.t = t; } @Override public void run() { System.out.println("beng,爆炸了"); t.cancel(); } }
定時任務的屢次執行代碼
package cn.itcast_12; import java.util.Timer; import java.util.TimerTask; /* * 定時器:可讓咱們在指定的時間作某件事情,還能夠重複的作某件事情。 * 依賴Timer和TimerTask這兩個類: * Timer:定時 * public Timer() * public void schedule(TimerTask task,long delay) * public void schedule(TimerTask task,long delay,long period) * public void cancel() * TimerTask:任務 */ public class TimerDemo2 { public static void main(String[] args) { // 建立定時器對象 Timer t = new Timer(); // 3秒後執行爆炸任務第一次,若是不成功,每隔2秒再繼續炸 t.schedule(new MyTask2(), 3000, 2000); } } // 作一個任務 class MyTask2 extends TimerTask { @Override public void run() { System.out.println("beng,爆炸了"); } }
定時刪除指定的帶內容目錄
package cn.itcast_12; import java.io.File; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask; /* * 需求:在指定的時間刪除咱們的指定目錄(你能夠指定c盤,可是我不建議,我使用項目路徑下的demo) */ class DeleteFolder extends TimerTask { @Override public void run() { File srcFolder = new File("demo"); deleteFolder(srcFolder); } // 遞歸刪除目錄 public void deleteFolder(File srcFolder) { File[] fileArray = srcFolder.listFiles(); if (fileArray != null) { for (File file : fileArray) { if (file.isDirectory()) { deleteFolder(file); } else { System.out.println(file.getName() + ":" + file.delete()); } } System.out.println(srcFolder.getName() + ":" + srcFolder.delete()); } } } public class TimerTest { public static void main(String[] args) throws ParseException { Timer t = new Timer(); String s = "2014-11-27 15:45:00"; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date d = sdf.parse(s); t.schedule(new DeleteFolder(), d); } }
多線程常見面試題
1:多線程有幾種實現方案,分別是哪幾種? 兩種。 繼承Thread類 實現Runnable接口 擴展一種:實現Callable接口。這個得和線程池結合。 2:同步有幾種方式,分別是什麼? 兩種。 同步代碼塊 同步方法 3:啓動一個線程是run()仍是start()?它們的區別? start(); run():封裝了被線程執行的代碼,直接調用僅僅是普通方法的調用 start():啓動線程,並由JVM自動調用run()方法 4:sleep()和wait()方法的區別 sleep():必須指時間;不釋放鎖。 wait():能夠不指定時間,也能夠指定時間;釋放鎖。 5:爲何wait(),notify(),notifyAll()等方法都定義在Object類中 由於這些方法的調用是依賴於鎖對象的,而同步代碼塊的鎖對象是任意鎖。 而Object代碼任意的對象,因此,定義在這裏面。 6:線程的生命週期圖 新建 -- 就緒 -- 運行 -- 死亡 新建 -- 就緒 -- 運行 -- 阻塞 -- 就緒 -- 運行 -- 死亡 建議:畫圖解釋。