线程收尾和网络编程

一、今日内容

1.课程回顾
2.作业讲解
3.线程交互
4.线程池
5.网络编程基础
6.IP和端口

二、课程回顾

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();
	}
}

四、线程通信

4.1 线程不通信的现象

卖包子的故事:边做、边卖。一个人:专门做包子,另一个专门卖包子。摊位:最多只能放20个包子。

多线程实现
线程收尾和网络编程_第1张图片

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();
	}
}
4.2 线程通信

线程通信:有一个缓冲区的仓库,生产者线程,不断往仓库存储内容,消费者线程不断从仓库中获取内容。为了解决生产者和消费者的动态调节的问题,可以使用线程通信机制,来保护生产者和消费中的同步。

防止出现一锅烩,资源严重浪费。

线程通信:

1.共享仓库

2.锁

3.使用wait和notity/notifyall 调整线程的状态

4.3 基于线程通信的实现

使用wait和notify 进行生产者消费者模型的动态调整

注意:wait、notify、notifyall 都是Object的方法、都需要使用在同步方法中。

notify和notifyall 区别?

notify :随机唤醒一个在当前锁上,因为wait方法而陷入阻塞的线程。

notifyall :唤醒所有在当前锁上,因为wait方法而陷入阻塞的线程。

如果是一对一 那么使用notify .如果是多对多就需要使用notifyall 。

wait:线程 状态从运行 变更为 阻塞状态

notify\notifyall:只能唤醒因为wait阻塞的线程。线程的状态从阻塞 变更为 就绪状态

线程收尾和网络编程_第2张图片

代码实战:模拟 做汉堡和卖汉堡

需求:汉堡店卖汉堡。容量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();
	}
}

五、线程池

5.1 线程池

线程池:有效并充分利用线程,合理分配资源

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

5.2 线程池演示

示例代码:手动创建线程池

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<>();	
	}
}

同步:按部就班,有序进行。排队 单线程

异步:同时执行,互不影响。多线程

六、网络基础

6.1 网络

计算机网络:就是把分布在不同区域的计算机,与专门的外部设备通信互联成为一个规模巨大,而且功能强大,网络系统,从而达到计算机之间可用相互通信,交换资源、共享信息等。

网络的分类:根据网络的范围可用分为:广域网、域域网、局域网

网络编程:是指在网络中,不同的机器之间进行通信,编程。

6.2 网络模型

OSI:Open System Interconnect 开放式系统互联. 参考 ISO(国际标准化组织)制定一种通用 计算机通信标准体系。七层模型。

线程收尾和网络编程_第3张图片

TCP/IP的四层模型:应用层、传输层、网络层、数据链路层

应用层(应用层、表示层、会话层):程序实现的细节

传输层:实现传输的通信,提供端对端的通信,只支持2种协议:TCP、UDP

数据链路层(数据链路层、物理层):驱动程序、网络接口卡等 硬件

6.3 IP和端口

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

6.4 Java的IP类

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());
		}
	}
}

你可能感兴趣的:(线程收尾和网络编程)