Day20-网络
1. 线程(续)
juc 中的大部分类是通过无锁并发实现的(没有用synchronized)
CAS 机制 compare And swap 比较并交换
synchronized 可以称之为悲观锁
cas 体现的是乐观锁
首先不会给共享资源加锁,而是做一个尝试
先拿到旧值,查看旧值是否跟共享区域的值相等
如果不等,那么说明别的线程改动了共享区域的值,我的修改失败
如果相等,那么就让我的修改成功
如果修改失败,没关系,重新尝试
int var5;
// 修改失败,没关系,重新尝试 自旋
do {
// 获取共享区域的最新值
var5 = this.getIntVolatile(var1, var2); // 10
// 比较并交换 最新值 最新值+1
} while(! this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
1. 重入锁 ReentrantLock
.lock() 加锁
.unlock() 解锁
例子:
static int i = 0;
public static void main(String[] args) throws InterruptedException {
ReentrantLock rl = new ReentrantLock();
Thread t1 = new Thread(() -> {
for (int j = 0; j < 5000; j++) {
try {
rl.lock(); // 加锁
i++;
} finally {
rl.unlock(); // 保证解锁一定被执行
}
}
});
Thread t2 = new Thread(() -> {
for (int j = 0; j < 5000; j++) {
try {
rl.lock(); // 加锁
i--;
} finally {
rl.unlock(); // 保证解锁一定被执行
}
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
synchronized 性能上比较 ReentrantLock 在高并发下低,ReentrantLock的内存占用会高一些
2. CountDownLatch
countdown 倒计时
当希望多个线程执行完毕后,再接着做下一步操作时,
例子:
public static void main(String[] args) throws InterruptedException {
CountDownLatch cdl = new CountDownLatch(3);// 构造方法需要指定倒计时的数字
new Thread(()->{
System.out.println("线程1开始运行"+new Date());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1准备完成"+new Date());
cdl.countDown();
}).start();
new Thread(()->{
System.out.println("线程2开始运行"+new Date());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程2准备完成"+new Date());
cdl.countDown();
}).start();
new Thread(()->{
System.out.println("线程3开始运行"+new Date());
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程3准备完成"+new Date());
cdl.countDown();
}).start();
// 主线程等待,直到倒计时为0
System.out.println("主线程等待");
cdl.await();
System.out.println("ready go....");
}
一个应用例子:模拟10个玩家加载进度
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(10);
String[] all = new String[10];
for (int j = 0; j < 10; j++) {
int x = j;
new Thread(()->{
Random r = new Random();
for (int i = 0; i <= 100; i++) {
try {
Thread.sleep(r.nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (all){
all[x]=(i+"%");
System.out.print("\r"+Arrays.toString(all));
}
}
latch.countDown();
}).start();
}
latch.await();
System.out.println("\nend...");
}
3. 循环栅栏
// CyclicBarrier 可循环的 屏障(栅栏)
// 当满足CyclicBarrier设置的线程个数时,继续执行,没有满足则等待
CyclicBarrier cb = new CyclicBarrier(2); // 个数为2时才会继续执行
new Thread(()->{
System.out.println("线程1开始.."+new Date());
try {
cb.await(); // 当个数不足时,等待
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("线程1继续向下运行..."+new Date());
}).start();
new Thread(()->{
System.out.println("线程2开始.."+new Date());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
cb.await(); // 2 秒后,线程个数够2,继续运行
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("线程2继续向下运行..."+new Date());
}).start();
与倒计时锁的区别:倒计时锁只能使用一次,倒计时结束这个对象就没用了。
而循环栅栏可以重复利用。
4. 信号量
Semaphore s = new Semaphore(3); // 限制了能同时运行的线程上限
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
s.acquire(); // 获得此信号量
System.out.println("我是线程" + Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
s.release(); // 释放信号量
}
}).start();
}
2. 网络模型与协议
OSI 七层模式 : 应用层,表示层,会话层,传输层,网络层,链路层,物理层
五层模型: 应用层, 传输层,网络层,链路层,物理层
四层模型 : 应用层, 传输层,网络层,链路层
应用层:http(超文本传输协议) ftp(文件传输协议) stmp (邮件发送协议) pop3(邮件接收协议), ssh ( 安全shell,用于远程登录)
传输层: tcp(安全可靠的协议) udp(不可靠)
网络层:ip
windows下可以使用 ipconfig
来查看ip地址
linux 下可以使用 ifconfig
来查看ip地址
ip 地址的作用是用来定位到网络上的另一台计算机
port 端口 mysql 3306
oracle 1521
sqlserver 1433
redis 6379
tomcat 8080
apache(http的服务) 80
ftp 21
ssh 22
qq客户端 8000
…
端口号的作用是用来标记,要访问对方的哪个程序
传输层协议:
tcp协议:
TCP 协议的特点是: TCP 协议是一个有连接、可靠的协议。所谓有连接,指的是在进行 TCP通信之前,两个需要通信的主机之间要首先建立一条数据通道,就好像打电话进行交流之前,首先要让电话接通一样。所谓可靠,指的是 TCP 协议能够保证: 1、发送端发送的数据不会丢失; 2、接收端接受的数据包的顺序,会按照发送端发送的包的顺序接受。也就是说, TCP协议能够保证数据能够完整无误的传输。
udp协议:
与 TCP 协议相比, UDP 是一个无连接,不可靠的协议。 即:数据的发送方只负责将数据发送出去,数据的接受方只负责接受数据。发送方和接收方不会相互确认数据的传输是否成功。
相对于 TCP 而言, UDP 有一个优点:效率较高。因此,当我们在对数据传输的正确率
不太关心,但是对传输效率要求较高的情况下,可以采用 UDP 协议。典型的使用 UDP 协议的是网络语音以及视频聊天应用。
3. java中的网络编程
Socket API 对tcp、udp协议做了封装,能够连接到对方主机,收发数据
tcp的例子
建立连接
服务器端:
public static void main(String[] args) throws IOException {
// 可以与客户端的socket建立连接 端口号一般使用4位以上的数字
ServerSocket ss = new ServerSocket(5555);
System.out.println("ready...等待客户端连接");
// 端点
Socket socket = ss.accept();// 等待客户端连接我服务器方法,直到有客户端连接
}
客户端:
public static void main(String[] args) throws IOException {
// 本机ip地址为 127.0.0.1
// 它的别名 localhost
// 端点
Socket socket =
new Socket("127.0.0.1", 5555);
}
传输数据
1.CS模型一
server端:
package socket;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
// 服务器端 Server
public class Server {
public static void main(String[] args) throws IOException {
// 可以与客户端的socket建立连接 端口号一般使用4位以上的数字
ServerSocket ss = new ServerSocket(5555);
System.out.println("ready...等待客户端连接");
// 端点
Socket socket = ss.accept();// 等待客户端连接我服务器方法,直到有客户端连接
InputStream is = socket.getInputStream();
byte[] buf = new byte[1024];
while(true) {
int len = is.read(buf);
if(len == -1){
break;
}
System.out.println(new String(buf,0,len));
}
socket.close();
}
}
Client端:
package socket;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.Scanner;
// 客户端 client
// 改进:从控制台输入信息
public class Client {
public static void main(String[] args) throws IOException {
// 本机ip地址为 127.0.0.1
// 它的别名 localhost
// 端点
Socket socket =
new Socket("127.0.0.1", 5555);
// socket.getOutputStream().write("hello".getBytes());
// 接收消息的线程
new Thread(()->{
try {
InputStream is = socket.getInputStream();
while(true) {
byte[] buf = new byte[1024];
int len = is.read(buf);
if(len == -1) {
break;
}
String content = new String(buf, 0, len);
System.out.println(content);
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
Scanner scanner = new Scanner(System.in);
while(true) {
// 从控制台反复读入内容
String line = scanner.nextLine();
socket.getOutputStream().write(line.getBytes());
}
// socket.close();
}
}
2.CS模型二:利用线程池处理多线程任务请求
server端:
package socket;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// 要支持多个客户端连接
public class Server2 {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(5555);
ExecutorService threadPool = Executors.newCachedThreadPool();
System.out.println("ready... 等待客户端连接");
while(true) {
Socket socket = ss.accept(); // 每连接一个新的用户调用一次accept
// 让线程池中的线程来处理这次请求
threadPool.submit(()->{
try {
InputStream is = socket.getInputStream();
SocketAddress address = socket.getRemoteSocketAddress();
byte[] buf = new byte[1024];
while(true) {
int len = is.read(buf);
if(len==-1){
break;
}
System.out.println(address+" "+new String(buf,0,len));
}
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
}
3.CS模型三:利用线程池处理多线程任务请求,并且记录每个客户的信息
server端:
package socket;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// 要支持多个客户端连接, 要记录所有的客户端socket,并给客户端socket发送信息
public class Server3 {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(5555);
ExecutorService threadPool = Executors.newCachedThreadPool();
// 集合用来保存所有的客户端socket
ConcurrentHashMap<Socket, SocketAddress> map = new ConcurrentHashMap<>();
System.out.println("ready... 等待客户端连接");
while(true) {
Socket socket = ss.accept(); // 每连接一个新的用户调用一次accept
map.put(socket, socket.getRemoteSocketAddress()); // 将客户socket存入map
// 让线程池中的线程来处理这次请求
threadPool.submit(()->{
try {
InputStream is = socket.getInputStream();
SocketAddress address = socket.getRemoteSocketAddress();
byte[] buf = new byte[1024];
while(true) {
int len = is.read(buf);
if(len==-1){
break;
}
String content = address + " " + new String(buf, 0, len);// 获取某个客户端发过来的消息内容
// System.out.println(address+" "+new String(buf,0,len));
// 遍历map集合
for (Socket clientSocket : map.keySet()) {
// 向客户端socket写入消息内容
OutputStream os = clientSocket.getOutputStream();
os.write(content.getBytes());
}
}
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
}
4.基于UDP的单身寂寞狗自言自语聊天
发送端sender:
public class ChatSender implements Runnable {
@Override
public void run() {
try {
DatagramSocket socket = new DatagramSocket();
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String line = null;
DatagramPacket packet = null;
System.out.print("root@~$:");
while((line = reader.readLine()) != null) {
packet = new DatagramPacket(line.getBytes(), 0, line.getBytes().length,
InetAddress.getLocalHost(), 9090);
socket.send(packet);
}
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
接收端sender:
public class ChatReceive implements Runnable {
@Override
public void run() {
try {
DatagramSocket socket = new DatagramSocket(9090);
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
boolean flag = true;
while(flag) {
socket.receive(packet);
System.out.println(packet.getAddress().getHostAddress() + " 对我说:" +
new String(buf, 0, packet.getLength()));
}
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
```Main测试类```
public class ChatMain {
public static void main(String[] args) {
ChatReceive receive = new ChatReceive();
new Thread(receive).start();
ChatSender sender = new ChatSender();
new Thread(sender).start();
}
}
“`
5.最牛逼的聊天室实现
server端:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Server {
final static ConcurrentHashMap<Socket, String> map = new ConcurrentHashMap<>();
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(5555);
ExecutorService executor = Executors.newCachedThreadPool();
System.out.println("Waiting for connecting...");
while(true) {
Socket s = ss.accept();
executor.execute(()->{
receive(s);
});
}
}
private static void dispatcher(Socket socket, int cmd , String content) throws IOException {
switch (cmd) {
case 0:
map.put(socket, content);
System.out.println("Client["+map.get(socket)+"] is connected...");
break;
case 1: {
String resp = Arrays.toString(map.values().toArray());
send(socket, resp);
break;
}
case 2: {
String resp = "["+map.get(socket)+"] say:" + content;
for (Socket s : map.keySet()) {
send(s, resp);
}
break;
}
case 3:
String[] array = content.split(" ");
String name = array[0];
String resp = "["+map.get(socket)+"] say:" + array[1];
for( Socket s : map.keySet()) {
String n = map.get(s);
if(n.equals(name)) {
send(s, resp);
}
}
break;
case 4:
System.out.println(content);
break;
}
}
private static void send(Socket socket, String content) throws IOException {
byte[] bytes = content.getBytes("utf-8");
int length = bytes.length;
OutputStream os = socket.getOutputStream();
os.write(length >> 8);
os.write(length);
os.flush();
os.write(bytes);
os.flush();
}
private static void receive(Socket socket) {
try{
InputStream is = socket.getInputStream();
while(true) {
// 先高后低
int r1 = is.read();
if(r1 == -1) {
break;
}
int r2 = is.read();
int length = (r1 << 8) + r2;
int cmd = is.read();
byte[] bytes = new byte[length];
is.read(bytes);
dispatcher(socket, cmd, new String(bytes, "utf-8"));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
System.out.println("Client["+map.get(socket)+"] has disconnected...");
if(socket != null) {
map.remove(socket);
if(!socket.isClosed()) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
client端:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.util.Scanner;
public class Client {
private static volatile boolean quit = false;
public static void main(String[] args) throws IOException {
Socket s = new Socket("127.0.0.1", 5555);
Scanner scanner = new Scanner(System.in);
System.out.println("Prepare a name for you:");
String nick = scanner.nextLine();
if (nick.length() == 0) {
nick = String.valueOf(s.getLocalPort());
}
register(s, nick);
new Thread(()->{
try {
receive(s);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
System.out.println("ok, 可以聊天了,您的昵称是:[" + nick + "]");
System.out.println("================================");
System.out.println("=========1.查看聊天室用户[:u]");
System.out.println("=========2.群发[:a 内容]");
System.out.println("=========3.私聊[:p 对方昵称 内容]");
System.out.println("=========4.测试[:t 内容]");
System.out.println("=========5.退出[:q]");
System.out.println("================================");
while (!quit) {
String content = scanner.nextLine();
if (content.equals(":u")) {
send(s, 1, "");
} else if (content.startsWith(":a")) {
String[] a = content.split(" ");
if (a.length < 2) {
System.out.println("格式不正确,请重新输入");
continue;
}
send(s, 2, a[1]);
} else if (content.startsWith(":p")) {
String[] a = content.split(" ");
if (a.length < 3) {
System.out.println("格式不正确,请重新输入");
continue;
}
send(s, 3, content.replaceFirst(":p ", ""));
} else if (content.startsWith(":t")) {
String[] a = content.split(" ");
if (a.length < 2) {
System.out.println("格式不正确,请重新输入");
continue;
}
send(s, 4, a[1]);
} else if (content.equals(":q")) {
if(!s.isClosed()) {
s.close();
}
} else if(quit) {
} else {
System.out.println("格式不正确,请重新输入");
}
}
}
private static void register(Socket socket, String nick) throws IOException {
send(socket, 0, nick);
}
private static void send(Socket socket, int cmd, String content) throws IOException {
byte[] bytes = content.getBytes("utf-8");
int length = bytes.length;
OutputStream os = socket.getOutputStream();
os.write(length >> 8);
os.write(length);
os.write(cmd);
os.flush();
os.write(bytes);
os.flush();
}
private static void receive(Socket socket) throws IOException{
try{
InputStream is = socket.getInputStream();
while(!quit) {
// 先高后低
int r1 = is.read();
if(r1 == -1) {
break;
}
int r2 = is.read();
int length = (r1 << 8) + r2;
byte[] bytes = new byte[length];
is.read(bytes);
System.out.println(new String(bytes, "utf-8"));
}
} catch (SocketException e) {
System.out.println("service has disconnected, please press any key continue...");
quit = true;
} catch (IOException e) {
e.printStackTrace();
} finally {
if(socket != null) {
if(!socket.isClosed()) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}