java線程從入門到精通
阿新 • • 發佈:2018-09-18
throw 怎麽辦 -m call delay file 值類型 system task
(1)線程的簡單應用
/* * 雖然我們可以理解同步代碼塊和同步方法的鎖對象問題,但是我們並沒有直接看到在哪裏加上了鎖,在哪裏釋放了鎖, * 為了更清晰的表達如何加鎖和釋放鎖,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(); } } 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(); } } } }
(2)死鎖
public class MyLock { // 創建兩把鎖對象 public static final Object objA = new Object(); public static final Object objB = new Object(); } public class DieLock extends Thread { private boolean flag; public DieLock(boolean flag) { this.flag = flag; } @Override public void run() { if (flag) { synchronized (MyLock.objA) { System.out.println("if objA"); synchronized (MyLock.objB) { System.out.println("if objB"); } } } else { synchronized (MyLock.objB) { System.out.println("else objB"); synchronized (MyLock.objA) { System.out.println("else objA"); } } } } } /* * 同步的弊端: * A:效率低 * B:容易產生死鎖 * * 死鎖: * 兩個或兩個以上的線程在爭奪資源的過程中,發生的一種相互等待的現象。 * * 舉例: * 中國人,美國人吃飯案例。 * 正常情況: * 中國人:筷子兩支 * 美國人:刀和叉 * 現在: * 中國人:筷子1支,刀一把 * 美國人:筷子1支,叉一把 */ public class DieLockDemo { public static void main(String[] args) { DieLock dl1 = new DieLock(true); DieLock dl2 = new DieLock(false); dl1.start(); dl2.start(); } }
(3)案例一
/* * 分析: * 資源類:Student * 設置學生數據:SetThread(生產者) * 獲取學生數據:GetThread(消費者) * 測試類:StudentDemo * * 問題1:按照思路寫代碼,發現數據每次都是:null---0 * 原因:我們在每個線程中都創建了新的資源,而我們要求的時候設置和獲取線程的資源應該是同一個 * 如何實現呢? * 在外界把這個數據創建出來,通過構造方法傳遞給其他的類。 * */ 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(); } } public class Student { String name; int age; } public class SetThread implements Runnable { private Student s; public SetThread(Student s) { this.s = s; } @Override public void run() { // Student s = new Student(); s.name = "林青霞"; s.age = 27; } } public class GetThread implements Runnable { private Student s; public GetThread(Student s) { this.s = s; } @Override public void run() { // Student s = new Student(); System.out.println(s.name + "---" + s.age); } }
(4)
/* * 分析: * 資源類: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(); } } public class Student { String name; int age; } 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++; } } } } 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); } } } }
(5)
/* * 分析: * 資源類: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(); } } public class Student { String name; int age; boolean flag; // 默認情況是沒有數據,如果是true,說明有數據 } 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有 } } } 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 } } } }
(6)
public class MyRunnable implements Runnable { @Override public void run() { for (int x = 0; x < 100; x++) { System.out.println(Thread.currentThread().getName() + ":" + x); } } } /* * 線程組: 把多個線程組合到一起。 * 它可以對一批線程進行分類管理,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()); } }
(7)
/* * 分析: * 資源類: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(); } } 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(); } } 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++; } } } public class GetThread implements Runnable { private Student s; public GetThread(Student s) { this.s = s; } @Override public void run() { while (true) { s.get(); } } }
(8)
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(); } } public class MyRunnable implements Runnable { @Override public void run() { for (int x = 0; x < 100; x++) { System.out.println(Thread.currentThread().getName() + ":" + x); } } }
(9)
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; } } 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(); } }
(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(); } } 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; } }
(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(); // 更有難度的 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(); } }
(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); } } 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,爆炸了"); } } 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(); } }
java線程從入門到精通