今天上午考完了計算機二級,也算卸掉了一個大包袱吧,希望能過!(其實也就考着玩的,不來點考試就要發霉了)
好了,趁着難得的考后休息時間我就接着上一次沒寫完的繼續更新吧。
上一篇文章——>Java核心之紛繁復雜的線程(一),歡迎大家一起探討呀。
上次我們講到通過實現Runnable接口或是直接繼承Thread類就可以自己創建線程了,這一次我們直接通過一些實戰項目來練練手吧!
題目如下:
實現控制台購物車
實現ShoppingCart 。需要實現以下功能:
ShoppingCartItem屬性有
private String name;// 商品名稱
private double price;// 商品價格
private double num;// 商品數量
a.添加商品。向購物車中添加商品,如果商品已經存在,那么更新數量(原有數量+新數量)
b.更新購物車某一商品的數量。
c.刪除已選購的某一商品
d.計算購物車中商品的總金額(商品×單價再求和)
代碼:
package ShoppingCart; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 封裝購物車處理的業務邏輯 * * @author Administrator * */ public class ShoppingCartBiz { //String只是代號,與ShoppingCartItem中的值無關 private Map<String, ShoppingCartItem> map; //封裝的意義? //這里封裝就是為了減少不必要的對象創建,減少內存消耗 public ShoppingCartBiz() { map = new HashMap<String, ShoppingCartItem>(); } // private Map<String,ShoppingCartItem>map= // new HashMap<String,ShoppingCartItem>(); /** * 添加商品 * @param name * @param price */ public void addItem(String name, double price) { ShoppingCartItem item; // ShoppingCartItem item=new ShoppingCartItem(); if (map.containsKey(name)) {// 商品已經購買過了 item = map.get(name);//為什么要賦值給item,難道item底層是數組 item.setNum(item.getNum() + 1); } else {// 商品沒有購買 item = new ShoppingCartItem(); item.setName(name); item.setPrice(price); item.setNum(1); map.put(name, item); } } /** * 將所有商品存放到List中 * @return */ public List<ShoppingCartItem> toList(){ List<ShoppingCartItem> list=new ArrayList<ShoppingCartItem>(); //在這里new的list是指要新建一個list對象 //用泛型約束了其中的list保證該list對象是屬於ShoppingCartItem類型的 for(String key:map.keySet()){ list.add(map.get(key)); //list啥都存是沒設置泛型嗎? //這里的map.get(key)是指要傳入對象 //至於對象里面的屬性我們並不關心 } return list; } }
package ShoppingCart; /** * 購物車商品項類 * * @author Administrator * */ public class ShoppingCartItem { private String name;// 商品名稱 private double price;// 商品價格 private int num;// 購買的商品數量 public String getName() { return name; } public void setName(String name) { this.name = name; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public int getNum() { return num; } public void setNum(int num) { this.num = num; } }
測試類:
package ShoppingCart; import java.util.List; public class TestShoppingCartItem { public static void main(String[] args) { ShoppingCartBiz biz=new ShoppingCartBiz(); biz.addItem("華為手機", 1999); biz.addItem("小米音響", 499); biz.addItem("小米音響", 499); List<ShoppingCartItem> list=biz.toList(); for(ShoppingCartItem item:list){ System.out.println(item.getName()); System.out.println(item.getPrice()); System.out.println(item.getNum()); } } }
這里還有幾道題,雖然都是一些基礎題,但我們可以通過不同的角度思考問題以形成一種發散式思維,這將大有裨益
package ShoppingCart; public class ClimbThread implements Runnable { private String name; private int time; private int number; public ClimbThread(String name, int time, int number) { this.name = name; this.time = time; this.number = number; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getTime() { return time; } public void setTime(int time) { this.time = time; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } public void run(){ for(int i=1;i<=this.number;i++){ try { // Thread.currentThread().sleep(1000); Thread.currentThread().sleep(this.time); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"已爬完第"+i+"個100米" +"還剩"+(this.number-i)*100+"米"); if(i==this.number){ System.out.println(Thread.currentThread().getName()+"到達終點!"); } } } }
package ShoppingCart; public class TestClimbThread{ public static void main(String[] args) { System.out.println("***********開始爬山***********"); ClimbThread cd=new ClimbThread("老年人",4000, 3); ClimbThread cd2=new ClimbThread("年輕人",1000,20); Thread t1=new Thread(cd); Thread t2=new Thread(cd2); t1.setName(cd.getName()); t2.setName(cd2.getName()); t1.start(); t2.start(); } }
這個題目蠻有意思的,我在此提供了兩種方法來解決這個問題
第一種
我們借用一下主線程即main()來實現多線程在同一方法類而可以調用join()來使當前線程直接進入等待狀態,再插入另一個線程運行,直到指定的線程完成為止
package ThreadWork;
public class Registration implements Runnable { @Override public void run() { for(int i=1;i<=20;i++){ System.out.println(Thread.currentThread().getName() +":"+i+"號病人在看病……"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { Thread t1=new Thread(new Registration()); t1.setName("特需號"); t1.start(); t1.setPriority(10); Thread main=Thread.currentThread(); main.setName("普通號"); main.setPriority(5); for(int i=1;i<=50;i++){ System.out.println(Thread.currentThread().getName() +":"+i+"號病人在看病……"); try { Thread.sleep(1000); } catch (InterruptedException e1) { e1.printStackTrace(); } if(i==10){ try { t1.join(); } catch (InterruptedException e) { e.printStackTrace(); } } } // try { // Thread.sleep(1000); // } catch (InterruptedException e) { // e.printStackTrace(); // } } }
下面這種方法則是將mian()和線程創建方法分別設置
package ThreadWork; /** * 掛號看病 * @author Administrator * */ public class CountNumb implements Runnable { private int num; private String name; private int time; public CountNumb(int num,String name,int time) { this.num=num; this.name=name; this.time=time; } @Override public void run() { for (int i = 1; i <= this.num; i++) { try { Thread.sleep(time); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(this.name+":"+i+"號病人在看病"); if(this.name.equals("普通號") && i==10) { try { Thread.currentThread().join(3000); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
package ThreadWork; /** * @author Administrator * */ public class TestCountNumb { public static void main(String[] args) { CountNumb r1=new CountNumb(10,"特需號",500); CountNumb r2=new CountNumb(50,"普通號",250); Thread t1=new Thread(r1); Thread t2=new Thread(r2); t1.setPriority(Thread.MAX_PRIORITY); t2.setPriority(Thread.MIN_PRIORITY); t1.start(); t2.start(); } }
雖然這種方法使代碼看起來更加清爽,但卻因為沒法直接使用join(),只能變相地通過預估停止時間來實現目標方法,然而這是有風險的,而且操作上並不方便
所以還是建議使用第一種方法。
剛剛上面說到了線程的方法,既然說到了這里,我就總結一下主要的線程方法,當然這肯定不是所有的方法,想要所有方法的建議直接去看API
上圖上圖!
這里的一些方法我會下次單獨講一下詳細的使用注意事項,就不在此贅述。
除了上面的一般簡單使用外,線程還有一個非常重要的機制,那就是線程同步
在這里我們將引入一個新的專有名詞——鎖,(LOCK)
此時需要掌握的知識點
1、線程同步
使用synchronized修飾的方法控制對類成員變量的訪問
synchronized就是為當前的線程聲明一個鎖
修飾方法:
訪問修飾符 synchronized 返回類型 方法名(參數列表){……}
或者
synchronized 訪問修飾符 返回類型 方法名(參數列表){……}
修飾代碼塊:
synchronized(鎖對象){}
這里我們直接看兩個實戰項目就比較好理解了
引入:
這里就是要防止黃牛搶太多的票,這里我用的是break跳出循環來實現黃牛線程的死亡,如果還有更好的方法歡迎評論留言
public class TicketGrabbing implements Runnable { private String name; public int count=10; @Override public void run() { while(true){ synchronized(this){ if(count>0){ try { Thread.currentThread().sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } if(Thread.currentThread().getName().equals("黃牛黨")){ System.out.println(Thread.currentThread().getName()+ "搶到第"+(11-count)+"張票,剩余"+--count+"張票!"); break; } System.out.println(Thread.currentThread().getName()+ "搶到第"+(11-count)+"張票,剩余"+--count+"張票!"); }else{ break; } } } } public String getName() { return name; } public void setName(String name) { this.name = name; } }
public class TestTicketGrabbing { public static void main(String[] args) { TicketGrabbing tc=new TicketGrabbing(); Thread t1=new Thread(tc,"桃跑跑"); Thread t2=new Thread(tc,"張票票"); Thread t3=new Thread(tc,"黃牛黨"); t3.start(); t1.start(); t2.start(); } }
下面是另一個題目,其實就是完成線程安全的實際操作
題目本身並不難,但卻可以作為上手練習的好素材
代碼部分:
public class RunningMan implements Runnable { private String name; @Override public void run() { synchronized(this){ System.out.println(Thread.currentThread().getName()+"拿到接力棒!"); for(int j=1;j<=10;j++){ try { Thread.currentThread().sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() +"跑完了"+(j*10)+"米"); if(j==10){ break; } } } } public String getName() { return name; } public void setName(String name) { this.name = name; } }
public class TestRunningMan { public static void main(String[] args) { RunningMan rm=new RunningMan(); for(int i=1;i<=10;i++){ Thread ti=new Thread(rm,i+"號"); ti.start(); try { ti.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
好了,今天就講這么多吧,其實每一次總結都要花費我一個多小時,不過這也是另一種學習過程,畢竟溫故能知新嘛。
給出一張總結圖,希望大家喜歡
回見