线程同步
案例 卖票
public class SellTicket implements Runnable{
private int tickets=100;
@Override
public void run() {
while(true) {
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + "正在出售" + tickets + "张票");
tickets--;
}
}
}
}
public class SellTicketDemo {
public static void main(String[] args) {
//创建SellTicket类的对象
SellTicket st = new SellTicket();
//创建三个Thread类的对象,把SellTicket对象作为构造方法的参数,并给出对应的窗口名称
Thread t1 = new Thread(st, "窗口1");
Thread t2 = new Thread(st, "窗口2");
Thread t3 = new Thread(st, "窗口3");
//启动线程
t1.start();
t2.start();
t3.start();
}
}
卖票案例的问题【理解】
卖票出现了问题
相同的票出现了多次
出现了负数的票
问题产生原因
线程执行的随机性导致的
同步代码块解决数据安全问题【应用】
安全问题出现的条件
- 是多线程环境
- 有共享数据
- 有多条语句操作共享数据
如何解决多线程安全问题呢?
基本思想:让程序没有安全问题的环境
怎么实现呢?
把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可
Java提供了同步代码块的方式来解决
同步代码块格式:
synchronized(任意对象)
{
多条语句操作共享数据的代码
}
同步方法解决数据安全问题【应用】
同步方法的格式
同步方法:就是把synchronized关键字加到方法上
修饰符 synchronized 返回值类型 方法名(方法参数)
{
方法体;
}
同步方法的锁对象是什么呢?
this
静态同步方法
同步静态方法:就是把synchronized关键字加到静态方法上
修饰符 static synchronized 返回值类型 方法名(方法参数)
{
方法体;
}
线程安全的类【理解】
- StringBuffer
线程安全,可变的字符序列
从版本JDK 5开始,被StringBuilder 替代。 通常应该使用StringBuilder类,因为它支持所有相同的操
作,但它更快,因为它不执行同步
- Vector
从Java 2平台v1.2开始,该类改进了List接口,使其成为Java Collections Framework的成员。 与新的集
合实现不同, Vector被同步。 如果不需要线程安全的实现,建议使用ArrayList代替Vector
- Hashtable
该类实现了一个哈希表,它将键映射到值。 任何非null对象都可以用作键或者值
从Java 2平台v1.2开始,该类进行了改进,实现了Map接口,使其成为Java Collections Framework的成员。 与新的集合实现不同, Hashtable被同步。 如果不需要线程安全的实现,建议使用HashMap代替Hashtable
Lock锁
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化
方法名 | 说明 |
---|---|
ReentrantLock() | 创建一个ReentrantLock的实例 |
加锁解锁方法
方法名 | 说明 |
---|---|
void lock() | 获得锁 |
void unlock() | 释放锁 |
Object类的等待和唤醒方法
方法名 | 说明 |
---|---|
void wait() | 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方 法 |
void notify() | 唤醒正在等待对象监视器的单个线程 |
void notifyAll() | 唤醒正在等待对象监视器的所有线程 |
生产者和消费者案例【应用】
网络编程
网络编程三要素
1. IP地址
要想让网络中的计算机能够互相通信,必须为每台计算机指定一个标识号,通过这个标识号来指定要接收数据的计算机和识别发送的计算机,而IP地址就是这个标识号。也就是设备的标识
2. 端口
网络的通信,本质上是两个应用程序的通信。每台计算机都有很多的应用程序,那么在网络通信时,如何区分这些应用程序呢?如果说IP地址可以唯一标识网络中的设备,那么端口号就可以唯一标识设备中的应用程序了。也就是应用程序的标识
3. 协议
通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则,这就好比在道路中行驶的汽车一定要遵守交通规则一样。在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。常见的协议有UDP协议和TCP协议
IP地址
IP地址分为两大类
IPv4:是给每个连接在网络上的主机分配一个32bit地址。按照TCP/IP规定,IP地址用二进制来表示,每个IP地址长32bit,也就是4个字节。例如一个采用二进制形式的IP地址是“11000000 1010100000000001 01000010”,这么长的地址,处理起来也太费劲了。为了方便使用,IP地址经常被写成十进制
的形式,中间使用符号“.”分隔不同的字节。于是,上面的IP地址可以表示为“192.168.1.66”。IP地址的这种表示法叫做“点分十进制表示法”,这显然比1和0容易记忆得多
IPv6:由于互联网的蓬勃发展,IP地址的需求量愈来愈大,但是网络地址资源有限,使得IP的分配越发紧张。为了扩大地址空间,通过IPv6重新定义地址空间,采用128位地址长度,每16个字节一组,分成8组十六进制数,这样就解决了网络地址资源数量不够的问题
DOS常用命令:
ipconfig:查看本机IP地址
ping IP地址:检查网络是否连通
特殊IP地址:
127.0.0.1:是回送地址,可以代表本机地址,一般用来测试使用
端口
用两个字节表示的整数,它的取值范围是0 ~ 65535。其中,0~1023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败
协议
InetAddress【应用】
InetAddress:此类表示Internet协议(IP)地址
方法名 | 说明 |
---|---|
static InetAddress getByName(Stringhost) | 确定主机名称的IP地址。主机名称可以是机器名称,也可以是IP地址 |
String getHostName() | 获取此IP地址的主机名 |
String getHostAddress() | 返回文本显示中的IP地址字符串 |
UDP通信程序
Java中的UDP通信
UDP协议是一种不可靠的网络协议,它在通信的两端各建立一个Socket对象,但是这两个Socket只是发送,接收数据的对象,因此对于基于UDP协议的通信双方而言,没有所谓的客户端和服务器的概念Java提供了DatagramSocket类作为基于UDP协议的Socket
UDP发送数据
构造方法
方法名 | 说明 |
---|---|
DatagramSocket() | 创建数据报套接字并将其绑定到本机地址上的任何可用端口 |
DatagramPacket(byte[] buf,int len,InetAddressadd,int port) | 创建数据包,发送长度为len的数据包到指定主机的指定端口 |
相关方法
方法名 | 说明 |
---|---|
void send(DatagramPacket p) | 发送数据报包 |
void close() | 关闭数据报套接字 |
void receive(DatagramPacket p) | 从此套接字接受数据报包 |
发送数据的步骤
- 创建发送端的Socket对象(DatagramSocket)
- 创建数据,并把数据打包
- 调用DatagramSocket对象的方法发送数据
- 关闭发送端
UDP接收数据【应用】
接收数据的步骤
- 创建接收端的Socket对象(DatagramSocket)
- 创建一个数据包,用于接收数据
- 调用DatagramSocket对象的方法接收数据
- 解析数据包,并把数据在控制台显示
- 关闭接收端
构造方法
方法名 | 说明 |
---|---|
DatagramPacket(byte[] buf, int len) | 创建一个DatagramPacket用于接收长度为len的数据包 |