1.线程调度 join yield sleep
2.线程常用方法
3.线程安全
锁:synchronized lock
4.死锁
锁的正当使用
5.单例模式
要求:
1.实现多窗口卖票 —基本要求 30
2.实现统计每个窗口卖了多少张票 —升级要求 60
3.要求把每张票的信息写出到文件中 —终极要求 90
示例代码:电影票类
package com.qfedu.homework;
import java.util.Date;
/**
* @ClassName: Ticket
* @Description: TODO 电影票类
* @author Feri
* @date 2020年5月27日
*
*/
public class Ticket {
private int no;//序号 唯一
private String name;//电影名称
private Date stime;//放映时间
private int row;//排
private int line;//列
private String sale;//出票窗口-人
private Date ctime;//出票日期
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getStime() {
return stime;
}
public void setStime(Date stime) {
this.stime = stime;
}
public int getRow() {
return row;
}
public void setRow(int row) {
this.row = row;
}
public int getLine() {
return line;
}
public void setLine(int line) {
this.line = line;
}
public String getSale() {
return sale;
}
public void setSale(String sale) {
this.sale = sale;
}
public Date getCtime() {
return ctime;
}
public void setCtime(Date ctime) {
this.ctime = ctime;
}
/**
* @param no
* @param name
* @param stime
* @param row
* @param line
* @param sale
*/
public Ticket(int no, String name, Date stime, int row, int line, String sale) {
super();
this.no = no;
this.name = name;
this.stime = stime;
this.row = row;
this.line = line;
this.sale = sale;
this.ctime=new Date();
}
public Ticket() {
super();
}
@Override
public String toString() {
//StringBuilder builder=new StringBuilder();
StringBuffer buffer=new StringBuffer();
buffer.append("*********老邢影院**********\r\n");
buffer.append("序号:"+no+"\r\n");
buffer.append("\t"+name+"\t\r\n");
buffer.append(" 放映时间:"+DateUtil.format(stime)+"\r\n");
buffer.append("座位号:"+row+"排 "+line+" 列\r\n");
buffer.append("出票窗口:"+sale+" 出票日期:"+DateUtil.format(ctime)+"\r\n");
buffer.append("*********我们欢迎你!**********\r\n");
return buffer.toString();
}
}
示例代码:日期工具类
package com.qfedu.homework;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
/**
* @ClassName: DateUtil
* @Description: TODO(这里用一句话描述这个类的作用)
* @author Feri
* @date 2020年5月27日
*/
public class DateUtil {
//格式化日期
public static String format(Date date) {
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm");
return sdf.format(date);
}
//获取日期 获取未来的日期
public static Date getTime(int days) {
//日历
Calendar calendar=Calendar.getInstance();
//添加指定的天
calendar.add(Calendar.DAY_OF_MONTH,days);
return calendar.getTime();
}
}
示例代码:卖票的线程接口类
package com.qfedu.homework;
import java.io.FileWriter;
import java.io.IOException;
/**
* @ClassName: TicketRunnable
* @Description: TODO(这里用一句话描述这个类的作用)
* @author Feri
* @date 2020年5月27日
*
*/
public class TicketRunnable implements Runnable{
private final int MAX_COUNT=1000;//最大的票数
private final int LINE_COUNT=10;//每排的列数
private String name;//电影的名称
private int currno;//记录当前的序号
//简单粗暴
private int c1,c2,c3;
/**
* @param name
*/
public TicketRunnable(String name) {
super();
this.name = name;
}
@Override
public void run() {
int i=0;
while(currno<MAX_COUNT) {
//防止出现线程安全的问题
synchronized (this) {
if(currno<MAX_COUNT) {
//计算行和列
int r=currno/LINE_COUNT+1;
currno++;
int l=currno%LINE_COUNT==0?LINE_COUNT:currno%LINE_COUNT;
Ticket t=new Ticket(currno,name,DateUtil.getTime(3),r,l,Thread.currentThread().getName());
System.out.println(t.toString());
System.out.println();
write(t.toString(),currno!=1);
//实时卖票统计
// System.out.println(t.getSale()+"卖了 "+(++i)+"张票");
// System.out.println();
//最终的卖票统计
switch (t.getSale()) {
case "海为科技园":c1++;break;
case "天丰利":c2++;break;
case "智慧科技园":c3++;break;
}
if(currno==MAX_COUNT) {
System.out.println("海为科技园:一共卖了 "+c1);
System.out.println("天丰利:一共卖了 "+c2);
System.out.println("智慧科技园:一共卖了 "+c3);
}
}
}
}
}
//封装的 保存文件的方法
private void write(String s,boolean isappend) {
FileWriter fw=null;
try {
fw=new FileWriter("ticketlog.txt",isappend);
fw.write(s);
fw.write("\r\n");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
try {
fw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
示例代码:测试主类
public class Ticket_Main {
public static void main(String[] args) {
//实现多窗口的卖票
//1.封装类 票类 搬砖
//2.多线程卖票
//3.多个线程 多个窗口
//4.把打印的内容 写入到文件中
TicketRunnable tr=new TicketRunnable("《骚磊的一天》");
//1.
Thread td1=new Thread(tr);
td1.setName("海为科技园");
td1.start();
Thread td2=new Thread(tr);
td2.setName("天丰利");
td2.start();
Thread td3=new Thread(tr);
td3.setName("智慧科技园");
td3.start();
}
}
卖包子的故事:边做、边卖。一个人:专门做包子,另一个专门卖包子。摊位:最多只能放20个包子。
public class Baozi {
private int currcount;//当前的数量
private int maxcount=20;//最大容量
//做包子
public void make() {
if(currcount<maxcount) {
currcount++;
System.out.println("做包子:库存"+currcount+"个");
}else {
System.out.println("----》库存已满,没法做包子");
}
}
//卖包子
public void sale() {
if(currcount>0) {
currcount--;
System.out.println("卖包子:库存"+currcount+"个");
}else {
System.out.println("****** 》库存清零,没法卖包子");
}
}
}
public class MakeRunnable implements Runnable{
private Baozi bz;
public MakeRunnable(Baozi b) {
// TODO Auto-generated constructor stub
bz=b;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
bz.make();
}
}
}
public class SaleRunnable implements Runnable{
private Baozi bz;
public SaleRunnable(Baozi b) {
// TODO Auto-generated constructor stub
bz=b;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
bz.sale();
}
}
}
public class Thread_Baozi {
public static void main(String[] args) {
//豪杰卖包子
Baozi bz=new Baozi();
//做包子
MakeRunnable mr=new MakeRunnable(bz);
Thread td1=new Thread(mr);
td1.start();
//卖包子
SaleRunnable sr=new SaleRunnable(bz);
Thread td2=new Thread(sr);
td2.start();
}
}
线程通信:有一个缓冲区的仓库,生产者线程,不断往仓库存储内容,消费者线程不断从仓库中获取内容。为了解决生产者和消费者的动态调节的问题,可以使用线程通信机制,来保护生产者和消费中的同步。
防止出现一锅烩,资源严重浪费。
线程通信:
1.共享仓库
2.锁
3.使用wait和notity/notifyall 调整线程的状态
使用wait和notify 进行生产者消费者模型的动态调整
注意:wait、notify、notifyall 都是Object的方法、都需要使用在同步方法中。
notify和notifyall 区别?
notify :随机唤醒一个在当前锁上,因为wait方法而陷入阻塞的线程。
notifyall :唤醒所有在当前锁上,因为wait方法而陷入阻塞的线程。
如果是一对一 那么使用notify .如果是多对多就需要使用notifyall 。
wait:线程 状态从运行 变更为 阻塞状态
notify\notifyall:只能唤醒因为wait阻塞的线程。线程的状态从阻塞 变更为 就绪状态
代码实战:模拟 做汉堡和卖汉堡
需求:汉堡店卖汉堡。容量10.做汉堡和卖汉堡。使用生产者和消费者模型 代码模拟汉堡店的买卖。
示例代码:汉堡店
public class FoodShop {
private String name;//店名
private int curr;//当前库存量
private int max=10;//最大的容量
//做
public synchronized void make() {
while(curr==max) {
try {
System.out.println("---->库存满了,生产者阻塞");
//当目前的库存满了,就等待:当前线程 运行--->阻塞 等待其他线程的唤醒
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
curr++;
System.out.println(Thread.currentThread().getName()+"----》做了一个汉堡,目前库存:"+curr);
notifyAll();//唤醒 因为wait方法而陷入等待的线程
}
//卖
public synchronized void sale() {
while(curr==0) {
try {
System.out.println("****>库存空了,消费者阻塞");
//当目前的库存空了,就等待:当前线程 运行--->阻塞 等待其他线程的唤醒
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
curr--;
System.out.println(Thread.currentThread().getName()+"*****> 卖了一个汉堡,目前库存:"+curr);
notifyAll();//唤醒 因为wait方法而陷入等待的线程
}
}
示例代码:线程测试类
public class Thread_Wait {
public static void main(String[] args) {
//模拟 生产者和消费者
FoodShop fs=new FoodShop();
//生产者线程接口
Runnable pr=new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
fs.make();
Thread.yield();
}
}
};
//消费者接口
Runnable cr=new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
fs.sale();
Thread.yield();
}
}
};
//模拟线程 做和卖
Thread td1=new Thread(pr);
td1.setName("做汉堡A ");
td1.start();
Thread td3=new Thread(pr);
td3.setName("做汉堡B ");
td3.start();
Thread td5=new Thread(pr);
td5.setName("做汉堡C ");
td5.start();
Thread td2=new Thread(cr);
td2.setName("卖汉堡001 ");
td2.start();
Thread td4=new Thread(cr);
td4.setName("卖汉堡002 ");
td4.start();
}
}
线程池:有效并充分利用线程,合理分配资源
1.控制线程数量
2.避免了频繁的创建和销毁线程
3.对线程进行统一的管理
1.Executors Java封装的创建线程池的工具类 现在已经被淘汰 因为很容易引起OOM异常
可以方便的创建如下线程池:
newFixedThreadPool 创建固定线程数量的线程池
newSingleThreadExecutor 创建只有一个线程的线程池
newCachedThreadPool 创建一个没有上限的线程池
newScheduledThreadPool 创建一个固定线程数量的线程池,这个线程池具备延迟、定时重复执行
newWorkStealingPool 创建一个抢占式
2.ThreadPoolExecutor 目前推荐手动创建线程池
通过构造函数创建线程池对象。
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue)
参数解读:
corePoolSize:设置最小的线程数量
maximumPoolSize:设置最大的线程数量
keepAliveTime:设置空闲线程多久被回收
unit:时间的单位
workQueue:任务队列。存储:添加到线程池,还未被执行的线程任务。
线程池运行线程的方法:execute
示例代码:手动创建线程池
public class MyPool {
public static void main(String[] args) {
//创建线程池 最小线程 3个。最多5个。任务堆积 40.多余的线程空闲10秒
ThreadPoolExecutor tpe=new ThreadPoolExecutor(3, 5, 10, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(40));
//执行线程任务
tpe.execute(()->{
System.out.println("1111");});
//线程执行
tpe.execute(()->{
while(true) {
try {
Thread.sleep(3000);
System.out.println("看电影中……");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
tpe.execute(()->{
while(true) {
try {
Thread.sleep(2000);
System.out.println("正在学习中……");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
//jdk 新增的方法 底层还是调用execute 主要适用于带返回值
tpe.submit(()->{
System.out.println("abc");});
System.out.println(tpe.getActiveCount());
//常见的线程池的任务队列
// new LinkedBlockingDeque();
// new PriorityBlockingQueue<>();
// new ArrayBlockingQueue<>(10);
// new SynchronousQueue<>();
}
}
同步:按部就班,有序进行。排队 单线程
异步:同时执行,互不影响。多线程
计算机网络:就是把分布在不同区域的计算机,与专门的外部设备通信互联成为一个规模巨大,而且功能强大,网络系统,从而达到计算机之间可用相互通信,交换资源、共享信息等。
网络的分类:根据网络的范围可用分为:广域网、域域网、局域网
网络编程:是指在网络中,不同的机器之间进行通信,编程。
OSI:Open System Interconnect 开放式系统互联. 参考 ISO(国际标准化组织)制定一种通用 计算机通信标准体系。七层模型。
TCP/IP的四层模型:应用层、传输层、网络层、数据链路层
应用层(应用层、表示层、会话层):程序实现的细节
传输层:实现传输的通信,提供端对端的通信,只支持2种协议:TCP、UDP
数据链路层(数据链路层、物理层):驱动程序、网络接口卡等 硬件
IP:IP地址是指互联网中的地址:Internet Protocol Address.
是互联网设备与互联网之间的唯一标记。同一个网段中,IP地址唯一。
IP地址的组成:是数字类型,是一个32位的整数。通常是为4个8进制的数字,每8位之间使用.隔开。
但是为了方便查看和传输IP地址,一般都是讲进制转换十进制。8个二进制转换为十进制:0-255之间。
eg:127.0.0.1 192.168.1.1
IP协议分为:IP4、IP6
IP地址分类:广域网
类型 | 范围 | 作用 |
---|---|---|
A类地址 | 1.0.0.1~126.255.255.254 | 保留给政府机构 |
B类地址 | 128.0.0.1~ 191.255.255.254 | 分配大中型企业 |
C类地址 | 192.0.0.1~ 223.255.255.254 | 任何人 |
D类地址 | 224.0.0.1~239.255.255.254 | 组播地址 |
E类地址 | 240.0.0.1~255.255.255.254 | 实验地址 |
回环地址 | 127.0.0.1 | 本机 |
IP地址可以定位计算机(手机、有网络的设备),那么端口就是定位具体的软件的
端口号:数据的发送和接收都需要通过端口出入计算机。
端口号用来唯一标记通信实体上运行的程序。同一个机器上,一个端口号只能对应一个运行程序。
端口号:就是一些数字,范围:0-65535
端口号的分类:
公共端口:0-1023 国际上被认证的端口号 比如:http协议:80 https:443 dns:53
注册端口:1024~49151 这块端口我们可以使用,但是个人建议使用10000以后的
动态、私有端口:49152~65535
常用软件对应的端口号:
Tomcat 8080
Mysql 3306
Oracle 1521
ftp 21
SSH 22
Redis 6379
java提供了操作IP地址的类,InetAddress。
示例代码:获取ip地址
public class Ipaddress_Main {
public static void main(String[] args) throws UnknownHostException {
//获取本机地址
InetAddress ia=InetAddress.getLocalHost();
//获取IP地址
System.err.println(ia.getHostAddress());
//获取计算机名称
System.err.println(ia.getHostName());
//获取其他的地址 比如域名
InetAddress[] arr=InetAddress.getAllByName("www.taobao.com");
for(InetAddress i:arr) {
System.err.println(i.getHostAddress());
}
}
}