day16
一:死锁问题
1.同步的弊端:
如果出现了嵌套锁,可能产生死锁(我等着你,你等着我,但都不会释放各自所持有的锁)
2.死锁问题:
3.解决方式
1. 从加锁顺序的角度:1. 让多线程嵌套加锁的顺序一致即可2. 从部分持有的角度解决1. 让一个线程要么一次持有它所需要的所有锁,要么一把锁都不持有(原子操作)- 将所有加锁操作放在一个同步代码快中
二:生产者消费者问题
1.线程间通信api:
- wait() 阻止自己- public final void wait()- 在其他线程调用此对象的notify()方法或notityall()方法前,导致当前线程等待- 唤醒条件:在同一个对象上调用其notify()或notifyAll()- 运行条件:当前线程必须拥有此对象监视器(即我们只能在当前线程所持有的synchronize代码块中调用wait方法)- 监视器:指synchronize代码块中的锁对象- 执行特征:1. 该线程发布(释放)对此监视器的所有权1. 等待(阻塞)1. 如果此时被唤醒,不是立刻执行而是等到 再持有锁再继续执行- notify() 通知别人- public final void notify()- 唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程- notifyall() 通知别人- public final void notifyall()- 唤醒在此对象监视器上等待的所有线程
2.生产者消费者实例
1. 定义生产者(用一个线程来模拟)1. 定义消费者(用一个线程来模拟)1. 定义蒸笼(共享缓冲区)1. 定义类描述包子1. edition21. 实现生产者和消费者之间的线程同步1. 实现生产者和消费者之间的通信(实现线程间的通信)- 为什么多个生产者线程和多消费者线程的时候使用notify方法,此时会出现卡死的情况- notify唤醒了本来不该唤醒的线程2. 虚假唤醒:- 一个本不应该被唤醒的线程被唤醒了,这就叫虚假唤醒- 所以为了保证在虚假唤醒的情况下,我们的生产者或消费者线程,仍然能正确执行,我们必须保证在每次执行之前,先判断蒸笼状态(先判断执行条件)
3.Thread.sleep和Object.wait()
1. 所属不同1. 唤醒条件不同1. 是否释放锁
4.生产者消费者问题(包子与蒸笼实例)
//消费者任务public class ConsumerTask implements Runnable {private Container container;public ConsumerTask(Container container) {this.container = container;}@Overridepublic void run() {while (true) {container.eatFood();}}}//生产者任务public class ProducerTask implements Runnable {private Container container;private Food[] foodsBill = {new Food("牛肉包", 3.0), new Food("杭州小笼包", 1.0),new Food("蟹黄包", 5.0), new Food("灌汤包", 4.0)};private Random random;public ProducerTask(Container container) {this.container = container;random = new Random();}@Overridepublic void run() {while (true) {// 做包子放入蒸笼int foodIndex = random.nextInt(foodsBill.length);container.setFood(foodsBill[foodIndex]);}}}//蒸笼public class Container {private Food food;public void setFood(Food newFood) {food = newFood;}public void eatFood() {food = null;}}class Food {String name;double price;public Food(String name, double price) {this.name = name;this.price = price;}@Overridepublic String toString() {return "Food{" +"name='" + name + '\'' +", price=" + price +'}';}}
网络编程:
一.网络编程概述
- 应用层产生待传输的数据传给传输层1. 通过IP端口发送(发送的是二进制字节数据)到接收端指定的端口,接收端传输层先接受1. 传输层将数据转交给应用层,应用层解析接受到的数据1. java语言中,已经定义好了来直接实现传输层的功能的工具类,只需关系实现传输层的类- ip:表示网络中的主句,即网络中的主机地址- 端口号:表示主机中进程的逻辑地址- 传输层协议:规定了数据传输的方式(TCP协议和UDP协议)- UDP协议:传输数据报包。- TCP协议:在传输端两端建立数据链接,利用流来进行数据传输- 在jdk中针对不同的传输层协议,实现了不同的类,按照不同的传输层协议定义了数据传输规则,实现跨进程数据传输。
二:基于UDP协议的网络传输
1.版本一
- 发送端代码:1. 建立UDP的socket对象1. 将要发送的数据封装成**数据报包**1. 通过UDP的socket对象,将数据包发送出1. 释放资源- DatagramSocket1. 构造方法:DatagramSocket(int port):创建数据报套接字并将其绑定到本地主机上的指定端口 //port就是我们指定的端口号- DatagramPacket 数据报包2. 构造方法:DatagramPacket(byte[] buf,int offset,int lenght,InetAddress address,int port)- buf 包数据- offset 包数据偏移量- length 包数据长度- address 目的地址- port 目的端口号
DatagramSocket socket = new DatagramSocket(10086); //创建UPD协议的套装子对象byte[] bytes = data.getBytes() //创建用于发送数据的数据报包 //data是应用层给的数据InetAddress targetIp = InetAddress.getByname("127.0.0.1");DatagramPacket sendPacket = new DatagramPacket(bytes,0,bytes.length,targerIp,1024);socket.send(sendPacket); //通过UDP的socket对象将数据报发出socket.close(); //关闭Socket并释放资源
- 接收端代码:1. 建立UDP的socket对象1. 创建用于接受数据的数据报包,通过socket对象的receive方法接受数据1. 通过数据包对象的功能来完成对接收到数据进行解析1. 可以对资源进行释放- DatagramPacket(byte[] buf, int offset, int lenght)- 应用层:- 解析收到的数据:receivePacket.getData() //返回一个字节数组,该字节数组中就存储了接收到的字节数据- getOffset 返回实际接收到的字节数据,在字节数组的起始位置- getLength 本次实际接收到的字节数据的个数- 注意事项:- public void receive(DatagramPacket p)- 从此套数据接收数据报包,当此方法返回时,DatagramPacket的缓冲区填充了接收的数据- 数据报包也包含发送方的IP地址和发送方机器上的端口号- 此方法在接收到数据前一直阻塞- 在一台主机中,一个端口号只能绑定一个进程- java.net.BindException:Address already in use: Cannot bind
DatagramSocket socket = new DatagramSocket(1024); //绑定本机IP和指定端口号的Socket对象byte[] byteBuf = new byte[1024];DatagramPacket receivePacket = new DatagramPacket(byteBuf, 0 , byteBuf.length);//创建用于接收数据的数据报包socket.receive(receivePacket); //通过socket对象的receive方法 接收数据socket.close(); //关闭socket对象,释放资源byte[] data = receivePacket.getData(); //返回一个字节数组,该自己数组中就存储了接收到的字节数据int offset = receivePacket.getOffset(); //本次实际接收到的字节数据,在字节数组的起始位置int length = receivePacket.getLenght(); //本次实际接收到的字节数据的个数
2.版本二
day17
4.版本四(同时接收数据) //简易聊天室的实现 (1.19(day 17)-1th)
- SendTask:- 关闭Socket对象为问题:- 约定不管是oneperson还是anotherperson,都由ReceiveTask来关闭
三:基于TCP协议的网络传输
1.Socket类(String host,int port)
- 创建一个流套接字并将其连接到指定主机上的指定端口号- 该Socket对象本身所绑定的ip地址和端口号:本机Ip 和 随机分配的一个端口号
2.创建过程
1. 发送端(基于Tcp的可靠的连接,无法单独运行客户端):1. 创建Socket对象 //Socket socket = new Socket(String host,int port)1. 从代表发送端的Socket对象中,获取用于发送数据的输出流 //OutputStream out = socket.getOutputSteam;1. 利用获取到的输出流发送数据 //out.write("".getBytes());1. 关闭Socket即可,Socket会负责关闭其中所持有的的流 //socket.close()2. 接收端(服务器端)1. 创建Serversocket对象,在指定端口监听客户连接请求 //Serversocket severSocket = new Serversocket(int port)1. 收到客户端连接请求后,建立Socket连接 // Socket accept = serverSocket.**accept()**1. 从代表本次连接中的服务器中的socket对象中,来获取输入流 // InputStream in = socket.getInputSteam()1. 关闭socket //两个socket都要关闭 socket.close() severSocket.close()- 服务器发送反馈消息给客户端,利用服务器端Socket的输出流- OutputStream out = socket.getOutputStream()
3.文件上传(练习)
//Clientpublic static void main(String[] args) throws IOException {// 创建客户端Socket对象Socket socket = new Socket("127.0.0.1", 10086);// 从本次连接中,代表客户端Socket对象,获取需要的流对象OutputStream out = socket.getOutputStream();BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out));// 缓冲字符输入流,读取源文件内容BufferedReader br = new BufferedReader(new FileReader("a.txt"));String line;while ((line = br.readLine()) != null) {bw.write(line);bw.newLine();}bw.flush();// 关闭发送端的输出流socket.shutdownOutput();// 接收服务器端发送的反馈消息InputStream in = socket.getInputStream();byte[] bytes = new byte[1024];// 阻塞方法,当没有接收到服务器端发送的数据,会阻塞等待int len = in.read(bytes);System.out.println(new String(bytes, 0, len));socket.close();br.close();}//Serverpublic static void main(String[] args) throws IOException {// 创建服务器端套接字对象ServerSocket serverSocket = new ServerSocket(10086);// 处理连接请求,并建立连接Socket socket = serverSocket.accept();InputStream in = socket.getInputStream();BufferedReader br = new BufferedReader(new InputStreamReader(in));FileWriter fileWriter = new FileWriter("c.txt");String line;// readLine方法因为底层使用socket.getInputStream()的read方法来读取while ((line = br.readLine()) != null) {// 第一种解决方案//if ("finish".equals(line)) {// break;//}fileWriter.write(line);fileWriter.write(System.lineSeparator());fileWriter.flush();}fileWriter.close();// 给客户端返送反馈消息,告诉客户端文件上传成功OutputStream out = socket.getOutputStream();out.write("文件上传成功".getBytes());socket.close();serverSocket.close();}
