多线程 网络编程 文件上传案例多线程
Day13 多线程
3).多线程的好处:
提高程序的运行效率,提高用户的体验度。 线程不会因为等待某个资源而进入等待状态
创建新的线程:
* 定义类继承Thread
* 重写方法run
* 创建Thread子类的对象
* 调用子类对象的方法 start()
*
* 为什么继承Thread
* Thread类是线程对象类
* 继承了Thread,子类也是线程对象
*
* 为什么重写run
* Sun工程师,不清楚其他人员用线程做什么
* 全部写在run中
*
* 为什么调用start
* 线程: JVM利用Windows中的功能实现
* start() 调用 本地方法,开启的线程
2.Thread类的方法,可以获取到线程名字
* String getName()
*
* Thread类方法 setName(String name)
* 设置线程名字
*
* 获取主线程名
* Thread类,定义静态方法
* static Thread currentThread() 返回当前线程
* 什么是当前线程: 现在运行的线程
第二种方式实现Runnable接口避免了单继承的局限性,所以较为常用。实现Runnable接口的方式,更加的符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。
第一种方式继承Thread类,线程对象和线程任务耦合在一起。一旦创建Thread类的子类对象,既是线程对象,有又有线程任务。
实现runnable接口,将线程任务单独分离出来封装成对象,类型就是Runnable接口类型。Runnable接口对线程对象和线程任务进行解耦。
3.创建线程程序第二个方式,接口方式
* java.lang.Runnable
* 定义类实现接口Runnable
* 重写抽象方法run
* 创建Thread类对象,Thread(Runnable r)
* 调用Thread对象start()方法
4. 售票代码,单独定义方法
* pay方法,所有的代码,全是线程操作的共享数据
* 同步整个方法,方法的定义修饰符,加同步
*
* 同步方法有锁吗,同步方法的锁就是this对象
*
* 静态同步方法有锁吗
* 静态方法的对象锁可不是this
* 锁是 本类.class对象
* Ticket.class
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算。就会导致线程安全问题的产生。
6.其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。
7.思考:线程对象调用 run方法和调用start方法区别?
线程对象调用run方法不开启线程,仅是对象调用方法。线程对象调用start开启线程,并让jvm调用run方法在开启的线程中执行。
2:请描述在什么样的情况下会出现线程安全的问题。
线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态
变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程
同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。
6.java中提供了线程同步机制,它能够解决上述的线程安全问题,请分别写出他们的格式
3.1同步代码块: 在代码块声明上 加上synchronized
synchronized (锁对象) {
可能会产生线程安全问题的代码
}
3.2同步方法:在方法声明上加上synchronized
public synchronized void method(){
可能会产生线程安全问题的代码
}
3.请描述线程的几个状态。
线程包含以下几个状态:
1. 新建状态:
调用构造方法创建线程对象后,线程就会处于新建状态。
2.就绪状态:
一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()
方法。在调用start()方法后,运行run()方法之前,线程就会处于就绪状态。
3.运行状态:
当线程获得CPU的资源后,才会进入运行状态,调用run方法。
4.阻塞状态:
当正在运行的线程还没有运行结束,暂时让出CPU资源,让其它处于就绪
状态的线程获得CPU资源。
5.死亡状态:
当线程的run方法运行结束后,线程就结束了,此时线程就会死亡状态。
- 7.多线程并发和多线程并行是什么呢?
答:两个或者多个任务发送请求时,CPU只能执行一个,就会安排这些任务交替执行,由于CPU做着高速的切换,间隔的时间比较短,我们看起来像同时执行的,这就是多线程并发。 并行是两个或多个任务同时执行,前提是多核CPU。 |
- 8.同步代码块和同步方法的锁是谁?
答;同步代码块的锁可以是任意类型的对象;非静态同步方法的锁是this;静态方法的锁是该类的字节码文件。 |
- 3.sleep和wait的区别?
答:(1)sleep是让线程睡眠,必须给相应的睡眠时间,不需要唤醒,时间到了会自动醒来,休眠时不放弃Cpu的执行权。(悲观锁机制) (2)wait的是让线程等待,可以传参也可以不传参,传参是在指定的时间后等待,需要被唤醒。等待的时候放弃cpu的执行权。(乐观锁机制) |
- 什么情况下需要同步?
答:当多线程并发, 有多段代码同时执行时, 我们希望某一段代码执行的过程中CPU不要切换到其他线程工作. 这时就需要同步. 如果两段代码是同步的, 那么同一时间只能执行一段, 在一段代码没执行结束之前, 不会执行另外一段代码. |
- 开启线程:开一个新的方法栈区
- 运行是方法run(
- 只要开启一个线程:
- 出现一个新的方法栈,运行
当调用一个线程对象start()方法后,此线程对象中的run()方法会被立即执行.(错误的)
Start()方法之后进入到”就绪状态”,等待操作系统分配cpu时间(正确的)
当一个线程对象的sleep()时间到了,会立即恢复运行(错误的)
当sleep()醒来后,会进入到”就绪状态”,等待操作系统分配cpu时间(正确的)
Day14 网络编程
InetAdderss类,该类用于封装一个IP地址,并提供了一系列与IP地址相关的方法,下表中列出了InetAddress类的一些常用方法。
TCP协议分哪几个层
链路层:链路层是用于定义物理传输通道,通常是对某些网络连接设备的驱动协议,例如针对光纤、网线提供的驱动。
网络层:网络层是整个TCP/IP协议的核心,它主要用于将传输的数据进行分组,将分组数据发送到目标计算机或者网络。
运输层:主要使网络程序进行通信,在进行网络通信时,可以采用TCP协议,也可以采用UDP协议。
应用层:主要负责应用程序的协议,例如HTTP协议、FTP协议等。
C/S 软件和服务器容易出现更新问题 , 一个更新全部需要更新,
B/S 网页和服务器 维护方便,客户端一个浏览器可以打开多个功能 弊端 所有浏览器内容存到浏览器
ip地址 它是由4个字节大小的二进制数来表示
HTTP、FTP、UDP、TCP
我们涉及到的:
1.UDP协议:
1.数据要打包发送;
2.数据大小有限制:64K
3.不论是否有接收端,都可以发送。也称为:无状态的,不安全的协议。
应用:视频广播,共享屏幕
2.TCP协议:
1.数据不需要打包,使用"流"的方式发送和接收;
2.数据大小无限制;
3.发送时,必须要有接收端;
1.InetAddress类表示一个IP地址;
2.InetAddress类没有构造方法,通过静态方法获取InetAddress对象;
3.常用方法:
静态方法:public static InetAddress getByName(String 计算机名/IP地址) :
普通方法:public String getHostAddress():获取本机IP
2.区别在于,UDP中只有发送端和接收端,不区分客户端与服务器端,计算机之间可以任意地发送数据。
而TCP通信是严格区分客户端与服务器端的,在通信时,必须先由客户端去连接服务器端才能实现通信,服务器端不可以主动连接客户端,并且服务器端程序需要事先启动,等待客户端的连接。
在JDK中提供了两个类用于实现TCP程序,一个是ServerSocket类,用于表示服务器端,一个是Socket类,用于表示客户端。
通信时,首先创建代表服务器端的ServerSocket对象,该对象相当于开启一个服务,并等待客户端的连接,然后创建代表客户端的Socket对象向服务器端发出连接请求,服务器端响应请求,两者建立连接开始通信。
public class TCPServer {
public static void main(String[] args) throws IOException {
//1,创建服务器,等待客户端连接
ServerSocket serverSocket = new ServerSocket(8888);
Socket clientSocket = serverSocket.accept();
//显示哪个客户端Socket连接上了服务器
InetAddress ipObject = clientSocket.getInetAddress();//得到IP地址对象
String ip = ipObject.getHostAddress(); //得到IP地址字符串
System.out.println("小样,抓到你了,连接我!!" + "IP:" + ip);
//7,获取Socket的输入流
InputStream in = clientSocket.getInputStream();
//8,创建目的地的字节输出流 D:\\upload\\192.168.74.58(1).jpg
BufferedOutputStream fileOut = new BufferedOutputStream(new FileOutputStream("D:\\upload\\192.168.74.58(1).jpg"));
//9,把Socket输入流中的数据,写入目的地的字节输出流中
byte[] buffer = new byte[1024];
int len = -1;
while((len = in.read(buffer)) != -1){
//写入目的地的字节输出流中
fileOut.write(buffer, 0, len);
}
//-----------------反馈信息---------------------
//10,获取Socket的输出流, 作用:写反馈信息给客户端
OutputStream out = clientSocket.getOutputStream();
//11,写反馈信息给客户端
out.write("图片上传成功".getBytes());
out.close();
fileOut.close();
in.close();
clientSocket.close();
//serverSocket.close();
}
}
public class TCPClient {
public static void main(String[] args) throws IOException {
//2,创建客户端Socket,连接服务器
Socket socket = new Socket("192.168.74.58", 8888);
//3,获取Socket流中的输出流,功能:用来把数据写到服务器
OutputStream out = socket.getOutputStream();
//4,创建字节输入流,功能:用来读取数据源(图片)的字节
BufferedInputStream fileIn = new BufferedInputStream(new FileInputStream("D:\\NoDir\\test.jpg"));
//5,把图片数据写到Socket的输出流中(把数据传给服务器)
byte[] buffer = new byte[1024];
int len = -1;
while ((len = fileIn.read(buffer)) != -1){
//把数据写到Socket的输出流中
out.write(buffer, 0, len);
}
//6,客户端发送数据完毕,结束Socket输出流的写入操作,告知服务器端 (socket流不能判断读到-1 结束读操作 )
socket.shutdownOutput();
//-----------------反馈信息---------------------
//12,获取Socket的输入流 作用: 读反馈信息
InputStream in = socket.getInputStream();
//13,读反馈信息
byte[] info = new byte[1024];
//把反馈信息存储到info数组中,并记录字节个数
int length = in.read(info);
//显示反馈结果
System.out.println( new String(info, 0, length) );
//关闭流
in.close();
fileIn.close();
out.close();
socket.close();
}
1.1 文件上传案例多线程版本
实现服务器端可以同时接收多个客户端上传的文件。
l 我们要修改服务器端代码
/*
* 文件上传多线程版本, 服务器端
*/
public class TCPServer {
public static void main(String[] args) throws IOException {
//1,创建服务器,等待客户端连接
ServerSocket serverSocket = new ServerSocket(6666);
//实现多个客户端连接服务器的操作
while(true){
final Socket clientSocket = serverSocket.accept();
//启动线程,完成与当前客户端的数据交互过程
new Thread(){
public void run() {
try{
//显示哪个客户端Socket连接上了服务器
InetAddress ipObject = clientSocket.getInetAddress();//得到IP地址对象
String ip = ipObject.getHostAddress(); //得到IP地址字符串
System.out.println("小样,抓到你了,连接我!!" + "IP:" + ip);
//7,获取Socket的输入流
InputStream in = clientSocket.getInputStream();
//8,创建目的地的字节输出流 D:\\upload\\192.168.74.58(1).jpg
BufferedOutputStream fileOut = new BufferedOutputStream(new FileOutputStream("D:\\upload\\"+ip+"("+System.currentTimeMillis()+").jpg"));
//9,把Socket输入流中的数据,写入目的地的字节输出流中
byte[] buffer = new byte[1024];
int len = -1;
while((len = in.read(buffer)) != -1){
//写入目的地的字节输出流中
fileOut.write(buffer, 0, len);
}
//-----------------反馈信息---------------------
//10,获取Socket的输出流, 作用:写反馈信息给客户端
OutputStream out = clientSocket.getOutputStream();
//11,写反馈信息给客户端
out.write("图片上传成功".getBytes());
客户端发送数据完毕,结束Socket输出流的写入操作,告知服务器端
out.close();
fileOut.close();
in.close();
clientSocket.close();
} catch(IOException e){
e.printStackTrace();
}
};
}.start();
}
//serverSocket.close();