线程和网络
线程
概念
进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程。比如在Windows系统中,一个运行的EXE就是一个进程。线程是指进程中的一个执行流程,一个进程中可以运行多个线程。比如java.exe进程中可以运行很多线程。线程总是属于某一个进程,进程中的多个线程共享进程内存。
实现
定义线程
- 扩展java.lang.Thread类。
- 实现java.lang.Runnable接口。
启动线程
在线程的对象上调用start()方法。
在调用start()方法之前,线程处于新状态中,还没有成为一个真正的线程。在调用start()之后,线程从新状态转移到可运行状态,当线程获得机会执行时,其目标run()方法将运行。
[外链图片转存失败(img-CvWHHHHA-1566632794265)(C:\Users\Eryu\AppData\Roaming\Typora\typora-user-images\1566626199146.png)]
新状态
已经创建线程对象。还没有在其上调用start()方法。
可运行状态
当线程有资格运行,但调度程序还没有把它选定为运行线程时线程所处的状态。当start()方法调用时,线程首先进入可运行状态,在线程运行之后或者从阻塞、等待或休眠状态回来后,也可以成为可运行状态。
运行状态
Runable状态中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态唯一的方式。
等待/阻塞/休眠状态
这是线程有资格运行时它所处的状态,三种线程实际上合为一种,线程任然是活的,但是当前没有条件运行。
死亡状态
当线程的run()方法完成时就认为它死去了。这个线程对象也许是活的,但是它已经不是一个单独执行的线程。线程一旦死亡,就不能复生。如果在一个死亡的线程上调用start()方法,就会java.lang.IllegalThreadStateException
线程同步
-
概念
线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。
-
锁的原理
Java中每个对象都有一个内置锁
当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this实例)有关的锁。获得一个对象的锁也称为获取锁、锁定对象、在对象上锁定或在对象上同步。
当程序运行到synchronized同步方法或代码块时才该对象锁才起作用。
一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放。
释放锁是指持锁线程退出了synchronized同步方法或代码块。
-
关于锁和同步,有一下几个要点:
- 只能同步方法,而不能同步变量和类;
- 每个对象只有一个锁;当提到同步时,应该清楚在什么上同步?也就是说,在哪个对象上同步?
- 如果两个线程要执行一个类中的synchronized方法,并且两个线程使用相同的实例来调用方法,那么一次只能有一个线程能够执行方法,另一个需要等待,直到锁被释放。也就是说:如果一个线程在对象上获得一个锁,就没有任何其他线程可以进入(该对象的)类中的任何一个同步方法。
- 线程睡眠时,它所持的任何锁都不会释放。
- 同步损害并发性,应该尽可能缩小同步范围。同步不但可以同步整个方法,还可以同步方法中一部分代码块。
- 在使用同步代码块时候,应该指定在哪个对象上同步,也就是说要获取哪个对象的锁。
-
举例:用多线程模拟3个窗口售卖火车票。
-
线程的安全性
当一个类已经很好的同步以保护它的数据时,这个类就称为“线程安全的”。
线程的互斥与死锁
-
wait()和notify()
程序执行wait()时,线程进入等待状态
程序执行notify()时,唤醒处于wait状态的线程 -
使用wait()和notify()的注意事项
wait()和notify()方法定义在Object类中,所以任何对象都可以调用,但是必须在synchronized同步方法或代码块才能调用,否则会产生异常。
要尽量避免死锁发生。如果程序中两条线程都进入wait()状态,那么彼此无法唤醒对方,就进入到了死锁。
如果进入wait状态的线程不止一条,调用notify()只能唤醒其中的一条,而且无法指定是哪一条。如果要全部唤醒,则可调用notifyAll()。
网络
两台或两台以上电脑通过网线连在一起就组成一个网络,网络编程就是两个或两个以上的设备之间的数据传输。
什么是网络通信协议?
网络通信协议就像我们的中文一样,她就是我们之间的共同语言,他规定了我们之间怎么说话,我先说什么你再说什么,你怎么说,而我应该怎么听。而网络通信得先规定约定一些俗成的网络通信协议,先说好,两台计算机之间什么收发信息,信息格式是什么,信息怎么发,怎么接收,而万一出错怎么办怎么处理。
OSI七层模型
[外链图片转存失败(img-hA2zh6AG-1566632794266)(file:///C:/Users/Eryu/Desktop/%E7%AC%94%E8%AE%B0/03.java%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E9%AB%98%E7%BA%A7/%E7%AC%94%E8%AE%B0/javaOOPhigh04_04.jpg)]
ISO制定的OSI参考模型的过于庞大、复杂招致了许多批评。与此对照,由技术人员自己开发的TCP/IP协议栈获得了更为广泛的应用。
[外链图片转存失败(img-DR6XDZzT-1566632794268)(file:///C:/Users/Eryu/Desktop/%E7%AC%94%E8%AE%B0/03.java%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E9%AB%98%E7%BA%A7/%E7%AC%94%E8%AE%B0/javaOOPhigh04_05.jpg)]
UDP和TCP
TCP和UDP都是传输层协议
网络应用中基本上都是TCP(Transmission Control Protocol传输控制协议)和UDP(User Datagram Protocol用户数据报协议),TCP是面向连接的通信协议,UDP是无连接的通信协议.
TCP协议特点
传输控制协议,提供的是面向连接、可靠的字节流服务。
当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。
TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。
三次握手
[外链图片转存失败(img-3DWrMXQb-1566632794270)(file:///C:/Users/Eryu/Desktop/%E7%AC%94%E8%AE%B0/03.java%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E9%AB%98%E7%BA%A7/%E7%AC%94%E8%AE%B0/javaOOPhigh04_03.png)]
UDP协议特点
UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。
UDP传输数据时是有大小限制的,每个被传输的数据报必须限定在64KB之内。
由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,所以传输速度很快。
应用
TCP在网络通信上有极强的生命力,例如远程连接(Telnet)和文件传输(FTP)都需要不定长度的数据被可靠地传输。但是可靠的传输是要付出代价的,对数据内容正确性的检验必然占用计算机的处理时间和网络的带宽 ,因此TCP传输的效率不如UDP高。
UDP操作简单,而且仅需要较少的监护,因此通常用于局域网高可靠性的分散系统中client/server应用程序。例如视频会议系统,并不要求音频视频数据绝对的正确,只要保证连贯性就可以了,这种情况下显然使用UDP会更合理一些。
[外链图片转存失败(img-MmbWK2lq-1566632794271)(file:///C:/Users/Eryu/Desktop/%E7%AC%94%E8%AE%B0/03.java%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E9%AB%98%E7%BA%A7/%E7%AC%94%E8%AE%B0/javaOOPhigh04_02.png)]
127.0.0.1是回路地址,用于测试,相当于localhost本机地址,没有网卡,不设DNS都可以访问.
端口地址在065535之间,其中01023之间的端口是用于一些知名的网络服务和应用,用户的普通网络应用程序应该使用1024以上的端口.
Socket连接套接字,Java分别为TCP和UDP提供了相应的类,TCP是java.net.ServerSocket(用于服务器端)和java.net.Socket(用于客户端);UDP是java.net.DatagramSocket.
Java编写UDP网络程序
类DatagramSocket介绍
DatagramSocket有如下构造方法:
- DatagramSocket() :构造数据报套接字并将其绑定到本地主机上任何可用的端口。
- DatagramSocket(int port):创建数据报套接字并将其绑定到本地主机上的指定端口。
- DatagramSocket(int port, InetAddress address):创建数据报套接字,将其绑定到指定的本地地址。即指定网卡发送和接收数据.
如果在创建DatagramSocket对象时,没有指定网卡的IP 地址,在发送数据时,底层驱动程序会自动选择一块网卡去发送,在接收数据时,会接收所有的网卡收到的与端口一致的数据.
发送信息时,可以不指定端口号,接收信息时,要指定端口号,因为要接收指定的数据.
发送数据使用DatagramSocket.send(DatagramPacket p)方法,接收数据使用DatagramSocket.receive(DatagramPacket p)方法.
类DatagramPacket介绍
DatagramPacket类有如下构造方法:
- DatagramPacket(byte[] buf, int length):构造 DatagramPacket,用来接收长度为length的数据包。
- DatagramPacket(byte[] buf, int length, InetAddress address, int port):构造数据报包,用来将长度为length的包发送到指定主机上的指定端口号。
接收数据时使用第一次构造方法,发送数据时使用第二种构造方法.
</