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();
}
}
}
}
好了,今天就讲这么多吧,其实每一次总结都要花费我一个多小时,不过这也是另一种学习过程,毕竟温故能知新嘛。
给出一张总结图,希望大家喜欢
回见
Java线程基础(二)的更多相关文章
- Java 线程基础
Java 线程基础
- 【Java并发专题之二】Java线程基础
使用线程更好的提高资源利用率,但也会带来上下文切换的消耗,频繁的内核态和用户态的切换消耗,如果代码设计不好,可能弊大于利. 一.线程 进程是分配资源的最小单位,线程是程序执行的最小单位:线程是依附于进 ...
- java线程基础知识----线程与锁
我们上一章已经谈到java线程的基础知识,我们学习了Thread的基础知识,今天我们开始学习java线程和锁. 1. 首先我们应该了解一下Object类的一些性质以其方法,首先我们知道Object类的 ...
- Java 线程基础知识
前言 什么是线程?线程,有时被称为轻量进程(Lightweight Process,LWP),是程序执行流的最小单元.一个标准的线程由线程 ID,当前指令指针 (PC),寄存器集合和堆栈组成.另外,线 ...
- Java线程基础实例
概述 Java线程是一个在实战开发中经常使用的基础功能,而在Java中线程相关的类在java.lang和java.util.concurrent里 Thread package thread.base ...
- JAVA线程基础
一.线程状态 由于参考的维度不一样,线程状态划分也不一样,我这里简单的分为5大类,并且会说明状态变迁的详细过程:
- Java线程基础知识(状态、共享与协作)
1.基础概念 CPU核心数和线程数的关系 核心数:线程数=1:1 ;使用了超线程技术后---> 1:2 CPU时间片轮转机制 又称RR调度,会导致上下文切换 什么是进程和线程 进程:程序运行资源 ...
- java线程基础知识----线程基础知识
不知道从什么时候开始,学习知识变成了一个短期记忆的过程,总是容易忘记自己当初学懂的知识(fuck!),不知道是自己没有经常使用还是当初理解的不够深入.今天准备再对java的线程进行一下系统的学习,希望 ...
- java线程基础巩固---线程生命周期以及start方法源码剖析
上篇中介绍了如何启动一个线程,通过调用start()方法才能创建并使用新线程,并且这个start()是非阻塞的,调用之后立马就返回的,实际上它是线程生命周期环节中的一种,所以这里阐述一下线程的一个完整 ...
- Java线程池二:线程池原理
最近精读Netty源码,读到NioEventLoop部分的时候,发现对Java线程&线程池有些概念还有困惑, 所以深入总结一下 Java线程池一:线程基础 为什么需要使用线程池 Java线程映 ...
随机推荐
- Nginx负载均衡后端健康检查(支持HTTP和TCP)
之前有一篇文章记录nginx负载均衡后端检查,链接为 https://www.cnblogs.com/minseo/p/9511456.html 但是只包含http健康检查不包含tcp下面安装ngin ...
- jmeter 之调试
目前知道的调试方法有两种:debug sample .http mirror server debug sample debug sample 的用户界面如下: 如果选择ture则表示打印对应的数据 ...
- springcloud第五步:使用Zuul搭建服务接口网关
路由网关(zuul) 什么是网关 Zuul的主要功能是路由转发和过滤器.路由功能是微服务的一部分,比如/api/user转发到到user服务,/api/shop转发到到shop服务.zuul默认和Ri ...
- 来吧学学.Net Core之项目文件简介及配置文件与IOC的使用
序言 在当前编程语言蓬勃发展与竞争的时期,对于我们.net从业者来说,.Net Core是风头正紧,势不可挡的.芸芸口水之中,不学习使用Core,你的圈内处境或许会渐渐的被边缘化.所以我们还是抽出一点 ...
- 2019春第八周作业Compile Summarize
这个作业属于那个课程 C语言程序设计II 这个作业要求在哪里 在这里 我在这个课程的目标是 能更加进一步的够熟练掌握指针的用法 这个作业在那个具体方面帮助我实现目标 指针对于高阶题目的做法 参考文献与 ...
- 【PY】Python3.7+Anaconda3 + PyQt5 + Eric6
Anaconda下载地址:https://www.continuum.io/downloads pip install pyenchant pip install QScintilla pip ins ...
- React browserHistory.push()传参
1.browserHistory.push({ "pathname":'/interface_log', state: ...
- Mac OS 安装robotframework
1,查看当前系统默认的Python路径 which python1==> /usr/bin/python 2,查看当前python 版本 python1==> Python 2.7.10 ...
- Docker:从头开始基于CentOS-Minimal安装Docker
基础环境:win10+vmware 14 一.CentOS-Minimal安装 虚拟机安装CentOS-Minimal的步骤不多说,网络选Net,硬件不需要的什么声卡打印机全都删掉没什么问题,然后ce ...
- Excle中的使用小技巧
关于从数据库中拷贝来的数字,拷贝到excle中,那些数字都变成了科学计算法. 步骤1,鼠标右键选中的列,选择“设置单元格格式(F)” 2.从这里面选中这些是否有小数,如果没有小数就把这个改成0