
今天上午考完了計算機二級,也算卸掉了一個大包袱吧,希望能過!(其實也就考着玩的,不來點考試就要發霉了)
好了,趁着難得的考后休息時間我就接着上一次沒寫完的繼續更新吧。
上一篇文章——>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();
}
}
}
}
好了,今天就講這么多吧,其實每一次總結都要花費我一個多小時,不過這也是另一種學習過程,畢竟溫故能知新嘛。
給出一張總結圖,希望大家喜歡

回見
