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(); } } } }
好了,今天就讲这么多吧,其实每一次总结都要花费我一个多小时,不过这也是另一种学习过程,毕竟温故能知新嘛。
给出一张总结图,希望大家喜欢
回见