標籤:oat hand let 匿名類 case current unlock mys tst
匿名類的一個好處是可以很方便的訪問外部的局部變數。
前提是外部的局部變數需要被聲明為final。(JDK7以後就不需要了)
======================
同步方法1:普通式
同步方法2:在對象方法裡 寫關鍵字,用this
同步方法3:在方法前,加上修飾符synchronized,效果等同方法2
==================
題目1--同步尋找檔案內容
把 練習-尋找檔案內容 改為多線程尋找檔案內容
原練習的思路是遍曆所有檔案,當遍曆到檔案是 .java的時候,尋找這個檔案的內容,尋找完畢之後,再遍曆下一個檔案
現在通過多線程調整這個思路:
遍曆所有檔案,當遍曆到檔案是.java的時候,建立一個線程去尋找這個檔案的內容,不必等待這個線程結束,繼續遍曆下一個檔案
package zsc.czy.zhonghe;import java.io.File;import java.io.FileReader;public class findContent { public static void main(String[] args) { File f = new File("d:/test"); search(f, "Hello"); } public static void search(File f, String search) { System.out.println(f); //d:\find.txt System.out.println(f.getAbsolutePath());// System.out.println(f.getName());//find.txt if (f.isFile()) { if (f.getName().toLowerCase().endsWith(".java")) { new Thread(new Runnable() { @Override public void run() { String fileContent = readFileConent(f); if (fileContent.contains(search)) { System.out.printf("找到子目標字串%s,在檔案:%s%n", search, f); } } }).start(); } } if(f.isDirectory()){ File[] fs = f.listFiles(); for(File file :fs){ search(file,search); } } } private static String readFileConent(File f) { try (FileReader fr = new FileReader(f);) { char[] c = new char[(int) f.length()]; fr.read(c); String s = new String(c); return s; } catch (Exception e) { return null; } }}
題目2--英雄充能
英雄有可以放一個技能叫做: 波動拳-a du gen。
每隔一秒鐘,可以發一次,但是只能連續發3次。
發完3次之後,需要充能5秒鐘,充滿,再繼續發。
package zsc.czy.thread;public class Hero { String name; int hp; int damage; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getHp() { return hp; } public void setHp(int hp) { this.hp = hp; } public int getDamage() { return damage; } public void setDamage(int damage) { this.damage = damage; } public boolean isDead() { return 0 >= hp ? true : false; } public void attackHero(Hero h) { try { // 為了表示攻擊需要時間,每次攻擊暫停1000毫秒 Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } h.hp -= damage; System.out.format("%s 正在攻擊 %s,%s的血編程了 %.0f%n", name, h.name, h.name, h.hp); if (h.isDead()) { System.out.println(h.name + "死了"); } } int totalTime = 3; public void adugen() { while (true) { for (int i = 0; i < totalTime; i++) { System.out.printf("波動拳第%d發%n", i + 1); try { Thread.sleep(1000); } catch (Exception e) { // TODO: handle exception } } System.out.println("開始為時5秒的充能"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { Hero h = new Hero(); h.name = "紅仔"; h.adugen(); }}
題目3--破解密碼
產生一個長度是3的隨機字串,把這個字串作為當做密碼
建立一個破解線程,使用窮舉法,匹配這個密碼
建立一個日誌線程,列印都用過哪些字串去匹配,這個日誌線程設計為守護線程
提示: 破解線程把窮舉法產生的可能密碼放在一個容器中,日誌線程不斷的從這個容器中拿出可能密碼,並列印出來。 如果發現容器是空的,就休息1秒,如果發現不是空的,就不停的取出,並列印。
==============================
HashMap不嚴格 可以存放 null --不是安全執行緒的類
Hashtable嚴格
ArrayList是非安全執行緒的
Vector是安全執行緒的類
====================
題目4-安全執行緒的MyStack
藉助把非安全執行緒的集合轉換為安全執行緒,用另一個方式完成 練習-安全執行緒的MyStack
題目5--死結
3個同步對象a, b, c
3個線程 t1,t2,t3
故意設計情境,使這3個線程彼此死結
//我這樣設計是不成功的package zsc.czy.thread;public class SiSuo { public static void main(String[] args) { final Hero ahri = new Hero(); ahri.name = "九尾妖狐"; final Hero annie = new Hero(); annie.name = "安妮"; final Hero leqing = new Hero(); annie.name = "李青"; Thread t1 = new Thread() { public void run() { // 佔有九尾妖狐 synchronized (ahri) { System.out.println("t1 已佔有九尾妖狐"); try { // 停頓1000毫秒,另一個線程有足夠的時間佔有安妮 Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("t1 試圖佔有安妮"); System.out.println("t1 等待中 。。。。"); synchronized (annie) { System.out.println("do something"); } } } }; t1.start(); Thread t2 = new Thread() { public void run() { // 佔有安妮 synchronized (annie) { System.out.println("t2 已佔有安妮"); try { // 停頓1000秒,另一個線程有足夠的時間佔有暫用九尾妖狐 Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("t2 試圖佔有李青"); System.out.println("t2 等待中 。。。。"); synchronized (leqing) { System.out.println("do something"); } } } }; t2.start(); Thread t3 = new Thread(){ public void run() { synchronized (leqing) { System.out.println("t3已佔有李青"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t3 試圖佔有九尾妖狐"); System.out.println("t3 等待中 。。。。"); } synchronized (ahri) { System.out.println("do something"); } }; }; t3.start(); }}
正確做法為:
package multiplethread; public class TestThread { public static void main(String[] args) { Object a = new Object(); Object b = new Object(); Object c = new Object(); Thread t1 =new Thread(){ public void run(){ synchronized (a) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } synchronized (b) { synchronized (c) { } } } } }; Thread t2 =new Thread(){ public void run(){ synchronized (c) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } synchronized (a) { synchronized (b) { } } } } }; Thread t3 =new Thread(){ public void run(){ synchronized (b) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } synchronized (c) { synchronized (a) { } } } } }; t1.start(); t2.start(); t3.start(); } }
================
使用wait和notify進行線程互動
this.wait()表示 讓佔有this的線程等待,並臨時釋放佔有
this.notify() 表示通知那些等待在this的線程,可以蘇醒過來了。
public synchronized void recover() { hp = hp + 1; System.out.printf("%s 回血1點,增加血後,%s的血量是%.0f%n", name, name, hp); // 通知那些等待在this對象上的線程,可以醒過來了,如第20行,等待著的減血線程,蘇醒過來 this.notify(); } // 掉血 同步方式和上面效果一樣 這個方式 public void hurt() { synchronized (this) { if (hp == 1) { // 讓佔有this的減血線程,暫時釋放對this的佔有,並等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } hp = hp - 1; System.out.printf("%s 減血1點,減少血後,%s的血量是%.0f%n", name, name, hp); } }
別忘了Thread 父類也是Object
題目--線程互動
假設加血線程運行得更加頻繁,英雄的最大血量是1000
設計加血線程和減血線程的互動,讓回血回滿之後,加血線程等待,直到有減血線程減血
// 回血 public synchronized void recover() { if(hp>=1000){ try { this.wait(); //後加的 } catch (InterruptedException e) { e.printStackTrace(); } } hp = hp + 1; System.out.printf("%s 回血1點,增加血後,%s的血量是%.0f%n", name, name, hp); // 通知那些等待在this對象上的線程,可以醒過來了,如第73行,等待著的減血線程,蘇醒過來 this.notify(); } // 掉血 同步方式和上面效果一樣 這個方式 public void hurt() { synchronized (this) { if (hp == 1) { // 讓佔有this的減血線程,暫時釋放對this的佔有,並等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } hp = hp - 1; System.out.printf("%s 減血1點,減少血後,%s的血量是%.0f%n", name, name, hp); //notify() 的意思是,通知一個等待在這個同步對象上的線程,你可以蘇醒過來了,有機會重新佔用當前對象了。 //掉血之後,喚醒等待的線程 this.notify();//後加的 } }
題目--多線程互動
在上面的練習的基礎上,增加回血線程到2條,減血線程到5條,同時運行。
運行一段時間,觀察會發生的錯誤,分析錯誤原因,並考慮解決辦法
題目--生產者消費者問題
生產者消費者問題是一個非常典型性的線程互動的問題。
- 使用棧來存放資料
1.1 把棧改造為支援安全執行緒
1.2 把棧的邊界操作進行處理,當棧裡的資料是0的時候,訪問pull的線程就會等待。 當棧裡的資料時200的時候,訪問push的線程就會等待
- 提供一個生產者(Producer)線程類,生產隨機大寫字元壓入到堆棧
- 提供一個消費者(Consumer)線程類,從堆棧中彈出字元並列印到控制台
- 提供一個測試類別,使兩個生產者和三個消費者線程同時運行,結果類似如下 :
==================
Lock
題目
在練習-安全執行緒的MyStack 練習中,使用synchronized把MyStack修改為了安全執行緒的類。
接下來,藉助Lock把MyStack修改為安全執行緒的類
把synchronized去掉
使用lock佔用鎖
使用unlock釋放鎖
必須放在finally執行,萬一heros.addLast拋出異常也會執行
package multiplethread; import java.util.LinkedList;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock; import charactor.Hero; public class MyStack { LinkedList<Hero> heros = new LinkedList<Hero>(); Lock lock = new ReentrantLock(); //把synchronized去掉 public void push(Hero h) { try{ //使用lock佔用鎖 lock.lock(); heros.addLast(h); } finally{ //使用unlock釋放鎖 //必須放在finally執行,萬一heros.addLast拋出異常也會執行 lock.unlock(); } } //把synchronized去掉 public Hero pull() { try{ //使用lock佔用鎖 lock.lock(); return heros.removeLast(); } finally{ //使用unlock釋放鎖 //必須放在finally執行,萬一heros.removeLast();拋出異常也會執行 lock.unlock(); } } public Hero peek() { return heros.getLast(); } public static void main(String[] args) { } }
題目--藉助tryLock 解決死結問題
當多個線程按照不同順序佔用多個同步對象的時候,就有可能產生死結現象。
死結之所以會發生,就是因為synchronized 如果佔用不到同步對象,就會苦苦的一直等待下去,藉助tryLock的有限等待時間,解決死結問題
package multiplethread; import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock; public class TestThread { public static void main(String[] args) throws InterruptedException { Lock lock_ahri = new ReentrantLock(); Lock lock_annie = new ReentrantLock(); Thread t1 = new Thread() { public void run() { // 佔有九尾妖狐 boolean ahriLocked = false; boolean annieLocked = false; try { ahriLocked = lock_ahri.tryLock(10, TimeUnit.SECONDS); if (ahriLocked) { System.out.println("t1 已佔有九尾妖狐"); // 停頓1000秒,另一個線程有足夠的時間佔有安妮 Thread.sleep(1000); System.out.println("t1 試圖在10秒內佔有安妮"); try { annieLocked = lock_annie.tryLock(10, TimeUnit.SECONDS); if (annieLocked) System.out.println("t1 成功佔有安妮,開始啪啪啪"); else{ System.out.println("t1 老是佔用不了安妮,放棄"); } } finally { if (annieLocked){ System.out.println("t1 釋放安妮"); lock_annie.unlock(); } } } } catch (InterruptedException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } finally { if (ahriLocked){ System.out.println("t1 釋放九尾狐"); lock_ahri.unlock(); } } } }; t1.start(); Thread.sleep(100); Thread t2 = new Thread() { public void run() { boolean annieLocked = false; boolean ahriLocked = false; try {annieLocked = lock_annie.tryLock(10, TimeUnit.SECONDS); if (annieLocked){ System.out.println("t2 已佔有安妮"); // 停頓1000秒,另一個線程有足夠的時間佔有安妮 Thread.sleep(1000); System.out.println("t2 試圖在10秒內佔有九尾妖狐"); try { ahriLocked = lock_ahri.tryLock(10, TimeUnit.SECONDS); if (ahriLocked) System.out.println("t2 成功佔有九尾妖狐,開始啪啪啪"); else{ System.out.println("t2 老是佔用不了九尾妖狐,放棄"); } } finally { if (ahriLocked){ System.out.println("t2 釋放九尾狐"); lock_ahri.unlock(); } } } } catch (InterruptedException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } finally { if (annieLocked){ System.out.println("t2 釋放安妮"); lock_annie.unlock(); } } } }; t2.start(); }}
題目--生產者消費者問題
在練習-生產者消費者問題這個練習中,是用wait(), notify(), notifyAll實現了。
接下來使用Condition對象的:await, signal,signalAll 方法實現同樣的效果
package multiplethread; import java.util.LinkedList;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock; public class MyStack<T> { LinkedList<T> values = new LinkedList<T>(); Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); public void push(T t) { try { lock.lock(); while (values.size() >= 200) { try { condition.await(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } condition.signalAll(); values.addLast(t); } finally { lock.unlock(); } } public T pull() { T t=null; try { lock.lock(); while (values.isEmpty()) { try { condition.await(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } condition.signalAll(); t= values.removeLast(); } finally { lock.unlock(); } return t; } public T peek() { return values.getLast(); }}
題目--使用AtomicInteger來替換Hero類中的synchronized
在給Hero的方法加上修飾符synchronized 這個知識點中,通過給hurt和 recover方法加上synchronized來達到安全執行緒的效果。
這一次換成使用AtomicInteger來解決這個問題
提示:int基本類型對應的是AtomicInteger,但是float基本類型沒有對應的AtomicFloat。 所以在這個練習中,把hp改為AtomicInteger即可。
java--線程--習題集錦