zoukankan      html  css  js  c++  java
  • 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();
    			}
    		}
    	}
    }
    

      

    好了,今天就讲这么多吧,其实每一次总结都要花费我一个多小时,不过这也是另一种学习过程,毕竟温故能知新嘛。

    给出一张总结图,希望大家喜欢

    回见

  • 相关阅读:
    flutter,跟着官网一步一步创建第一个flutter应用
    meta标签的用处详解
    js array 对象
    javascript异步编程,promise概念
    jquery常用
    《java编程思想》:异常丢失
    Nginx均衡负载配置
    Maven将jar包安装到本地仓库
    《java编程思想》读后笔记:二,吸血鬼数字
    基于jQuery Ajax实现无刷新文件上传
  • 原文地址:https://www.cnblogs.com/SUN99bk/p/10629037.html
Copyright © 2011-2022 走看看