锁升级
锁的四种状态:无锁、偏向锁、轻量级锁、重量级锁(JDK1.6)
无锁:操作数据时不会上锁
偏向锁:会偏向于第一个访问锁的线程, 如果在运行过程中,只有一个线程访问加锁的资源,不存在多线程竞争的情况,那么线程是不需要重复获取锁的,这种情况下,就会给线程加一个偏向锁。
偏向锁的实现是通过控制对象
Mark Word
的标志位来实现的,如果当前是可偏向状态
,需要进一步判断对象头存储的线程 ID 是否与当前线程 ID 一致,如果一致直接进入。
轻量级锁:当线程竞争比较激烈时,偏向锁就会升级称为轻量级锁,其他线程会通过
自旋方式
(空转)等待上一个线程释放锁
重量级锁:当线程的并发程度加剧,线程的
自旋
超过了一定次数,或者一个线程在自旋,又进来一个线程访问,轻量级锁就会升级成为重量级锁,重量级锁会把除了此时拥有锁的线程以外的线程都阻塞。升级到重量级锁其实就是互斥锁了,一个线程拿到锁,其余线程都会处于阻塞等待状态 。
网络编程
- Socket套接字
Socket
是 Java 网络编程中用于实现客户端和服务器之间通信的类。它提供了一种方法来建立客户端和服务器之间的连接,并通过读写流来进行数据的传输。在客户端中,可以使用
Socket
对象来创建一个与服务器建立连接的套接字。通过指定服务器的主机名和端口号,Socket
对象会尝试与服务器建立连接。一旦连接成功,就可以使用相关的输入流和输出流来进行数据的读写。在服务器端中,可以使用
ServerSocket
来监听指定的端口,一旦有客户端发起连接请求,ServerSocket
会返回一个Socket
对象,表示与客户端建立的套接字连接。通过这个Socket
对象,可以进行数据的读写和与客户端进行通信。总结来说,
Socket
类提供了一种机制,使客户端和服务器能够进行通信,通过读写流来交换数据。客户端和服务器可以使用Socket
对象进行连接建立、数据传输和通信操作。
这是一个简单的用户端与服务器交互的示例:
package com.wz.Test;
import java.io.Serializable;
public class Message<T> implements Serializable {
private String action;
private T data;
public Message(String action, T data) {
this.action = action;
this.data = data;
}
public String getAction() {
return action;
}
public T getData() {
return data;
}
}
package com.wz.Test;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
private ServerSocket serverSocket;
public Server(int port) throws IOException {
//构建了一个服务器的套接字对象,这个对象侦听的端口就是给定的端口
serverSocket = new ServerSocket(port);
}
public void start() {
while (true) {//死循环
try {//等待客户端连接,如果没有客户端连接,代码就阻塞在这里了
Socket client = serverSocket.accept();
System.out.println("客户端来连接了");
InputStream in = client.getInputStream();
ObjectInputStream ois = new ObjectInputStream(in);
Message<?> message = (Message<?>) ois.readObject();
String action = message.getAction();
System.out.println(action);
Object data = message.getData();
System.out.println(data);
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
public static void main(String[] args) {
try {
Server server = new Server(9000);
server.start();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
这段代码是一个简单的服务器端程序,它监听指定端口并接受客户端的连接。
- 创建
ServerSocket
对象来监听指定的端口。- 通过调用
accept()
方法来等待客户端的连接。一旦有客户端连接上来,accept()
方法会返回一个Socket
对象,表示与客户端建立的套接字连接。- 在
start()
方法中,通过获取客户端连接的输入流,并创建相应的对象输入流来读取客户端发送过来的消息。这里使用了ObjectInputStream
来读取Message<?>
对象,然后通过getAction()
和getData()
方法获取消息的操作和数据。
package com.wz.Test;
import java.io.*;
import java.net.Socket;
public class Client {
public static void main(String[] args) {
/*host的表示方式:
localhost,127.0.0.1,IP地址
构建一个客户端的套接字,这个套接字要去连接的是服务器套接字
同IP来定位计算机,通过端口号来定位应用,套接字对象一旦创建成功
也就是与服务器套接字连接上了*/
try {
Socket client = new Socket("localhost",9000);
//发送信息,输出流
OutputStream os = client.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(new Message<>("login","admin:123456"));
oos.flush();
client.shutdownOutput();//关闭输出通道,告诉服务器,信息已经传输完毕
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
这段代码是一个简单的客户端程序,它与上面的服务器端程序进行通信。
首先创建一个客户端的套接字对象
client
,并通过指定服务器的主机名和端口号来进行连接。这里使用的是本地主机 “localhost”,端口号为 9000。通过获取客户端套接字的输出流,并创建对象输出流来发送消息到服务器。使用
ObjectOutputStream
的writeObject()
方法向输出流写入消息对象,这里是一个Message<>
对象。调用
flush()
方法将缓冲区中的数据立即发送到服务器。调用
client.shutdownOutput()
方法关闭输出通道,告诉服务器信息已经传输完毕。
通过运行以上的服务器端程序和客户端程序,可以建立起客户端与服务器端的连接,并进行消息的传输和交互。
结果: