Java線程基礎(二)


今天上午考完了計算機二級,也算卸掉了一個大包袱吧,希望能過!(其實也就考着玩的,不來點考試就要發霉了)

好了,趁着難得的考后休息時間我就接着上一次沒寫完的繼續更新吧。

上一篇文章——>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();
			}
		}
	}
}

  

好了,今天就講這么多吧,其實每一次總結都要花費我一個多小時,不過這也是另一種學習過程,畢竟溫故能知新嘛。

給出一張總結圖,希望大家喜歡

 

 

回見

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM