网络编程 什么是计算机网络 eg:信封 写信 讲述包的概念（对方的信息，地址；自己的地址） eg: 打电话 --连接-- 接了 --通话 TCP 发短信 --发送了就完事了 --接受 UDP 计算机网络 计算机网络（英语：computer network），通常也简称网络，是指容许节点分享资源的数字电信网络[1]:1-3。在电脑网络，电脑设备会透过节点之间的连接（数据链路）互相交换数据。传输介质可分为有线及无线两类——有线的可用到双绞线、光纤电缆等介质[1]:1-4；无线则可用到Wi-Fi、NFC[2]:4-32。 网络编程的目的 无线电台 传播交流信息 数据交换 通信 要达到效果所需的 如何准确定位网络上的一台主机 eg：192.168.16.124 : 端口 ，定位到这个计算机上的某个资源 找到这个主机，如何传输数据？ 传输介质 javaweb：网页编程 网络编程：TCP/IP C/S 网络通信的两个要素 如何实现网络的通信？ 通信双方的地址： ip 端口号 eg：192.168.16.124：5900 规则：网络通信的协议 TCP/IP 参考模型 网络编程中两个主要的问题 如何准确定位到网络上的一台或者多台主机 找到主机之后如何进行通信 网络编程的要素 IP 和 端口号 ip 网络通信协议 udp，tcp 万物皆对象 ---Java IP地址 IP地址：InetAddress 唯一定位一台网络上计算机 127.0.0.1 ： 本机localhost ip地址的分类 IPv4/IPv6 IPv4 127.0.0.1 4个字节组成。 0~255，42亿个；30亿都在北美，亚洲4亿；2011年用尽IPv4 IPv6 fe80::d06b:cf88:8c85:7e09%9 128位；8个无符号整数！ eg: 2001:0bb2:aaaa:0015:0000:0000:1aaa:1321 公网（互联网） 私网（局域网） ABCD类地址 192.168.xxx.xxx 专门给组织内部使用的 域名：IP地址不容易记忆，用域名更方便 eg：我的blog域名为 www.kok-s0s.top package lesson01; import java.net.InetAddress; import java.net.UnknownHostException; //测试ip public class TestInetAddress { public static void main(String[] args) { try { //查询本机地址 InetAddress inetAddress1 = InetAddress.getByName("127.0.0.1"); System.out.println(inetAddress1); InetAddress inetAddress3 = InetAddress.getByName("localhost"); System.out.println(inetAddress1); InetAddress inetAddress4 = InetAddress.getLocalHost(); System.out.println(inetAddress1); //查询我的blog地址 InetAddress inetAddress2 = InetAddress.getByName("www.kok-s0s.top"); System.out.println(inetAddress2); //常用方法 System.out.println(inetAddress2.getAddress()); System.out.println(inetAddress2.getCanonicalHostName()); System.out.println(inetAddress2.getHostAddress()); System.out.println(inetAddress2.getHostName()); } catch (UnknownHostException e) { e.printStackTrace(); } } } 端口Port 端口表示计算机上的一个程序的进程： 不同的进程有不同的端口号！用来区分软件！ 规定范围 0~65535 * 2 可以 TCP:80 UDP:80 同时存在，但单个协议下，端口号不能冲突 端口分类 公有端口 0~1023 HTTP : 80 HTTPS : 443 FTP : 21 Telent : 23 程序注册端口：1024~49151，分配给用户或者程序 Tomcat : 8080 MySQL : 3306 Oracle : 1521 动态，私有：49152~65535 netstat -ano #查看所有的端口 netstat -ano|findstr "5900" #查看指定的端口 tasklist|findstr "8696" #查看指定端口的进程 Ctrl + Shift + ESC #快速打开任务管理器 package lesson01; import java.net.InetSocketAddress; public class TestInetSocketAddress { public static void main(String[] args) { InetSocketAddress inetSocketAddress1 = new InetSocketAddress("127.0.0.1", 8080); InetSocketAddress inetSocketAddress2 = new InetSocketAddress("localhost", 8080); System.out.println(inetSocketAddress1); System.out.println(inetSocketAddress2); System.out.println(inetSocketAddress1.getAddress()); System.out.println(inetSocketAddress1.getHostName());//地址 System.out.println(inetSocketAddress1.getPort());//端口 } } 通信协议 协议：约定，好比人们之间交流使用的普通话一样 网络通信协议：速率，传输码率，代码结构，传输控制······ 问题：有很多复杂的问题 能够大事化小：分层！ TCP/IP协议簇：一组协议 重要： TCP：用户传输协议 UDP：用户数据报协议 IP：网络互联协议 TCP与UDP的对比： TCP：打电话 连接，稳定 ‘三次握手’ ‘四次握手’ 最少需要三次，保证稳定连接！ A：你瞅啥？ B: 瞅你咋地？ A: 干一场！ A：我要走了 B：你真的要走了吗？ B: 你真的要走了！ A：我真的要走了！ 客户端 服务端 传输完成，释放连接，效率低 UDP：发短信 不连接，不稳定 客户端，服务端，没有明确的界限 不管有没有准备好，都可以发给你 DDOS：洪水攻击！（饱和攻击） TCP实现聊天 客户端 连接服务器 Socket 发送消息 package lesson02; import java.io.IOException; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; //客户端 public class TcpClientDemo01 { public static void main(String[] args) { Socket socket=null; OutputStream os=null; try { //1. 要知道服务器的地址 InetAddress serverIP = InetAddress.getByName("127.0.0.1"); int port = 9999; //2. 创建一个socket连接 socket = new Socket(serverIP, port); //3. 发送消息 IO流 os = socket.getOutputStream(); os.write("kok-s0s,你好".getBytes()); } catch (IOException e) { e.printStackTrace(); } finally { if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } if (os != null) { try { os.close(); } catch (IOException e) { e.printStackTrace(); } } } } } 服务器 建立服务的端口 ServerSocket 等待用户的连接 accept 接受用户的消息 package lesson02; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; //服务端 public class TcpServerDemo01 { public static void main(String[] args) throws IOException { ServerSocket serverSocket = null; Socket socket = null; InputStream is = null; ByteArrayOutputStream baos = null; try { //1. 客户端想知道我的地址，那我需要有一个地址 serverSocket = new ServerSocket(9999); while (true) { //2. 等待客户端请求连接 socket = serverSocket.accept(); //3. 读取客户端的消息 is = socket.getInputStream(); //管道流 baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; while ((len = is.read(buffer)) != -1) { baos.write(buffer, 0, len); } System.out.println(baos.toString()); } } catch (IOException e) { e.printStackTrace(); } finally { //关闭资源 if (baos != null) { try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } if (serverSocket != null) { try { serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } } } TCP文件上传实现 下面的代码没有做异常处理 服务端 package lesson02; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class TcpServerDemo02 { public static void main(String[] args) throws Exception { //1. 创建服务 ServerSocket serverSocket = new ServerSocket(9000); //2. 监听客户端的连接 Socket socket = serverSocket.accept();//阻塞式监听，会一直等待客户端连接 //3. 获取输入流 InputStream is = socket.getInputStream(); //4. 文件输出 FileOutputStream fos = new FileOutputStream(new File("receive.jpg")); byte[] buffer = new byte[1024]; int len; while ((len = is.read(buffer)) != -1) { fos.write(buffer, 0, len); } //通知客户端 接受完毕了 OutputStream os = socket.getOutputStream(); os.write("接受完毕了，你可以断开了".getBytes()); //5. 关闭资源 os.close(); fos.close(); is.close(); socket.close(); serverSocket.close(); } } 客户端 package lesson02; import java.io.*; import java.net.InetAddress; import java.net.Socket; public class TcpClientDemo02 { public static void main(String[] args) throws Exception { //1. 创建一个socket连接 Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9000); //2. 创建一个输出流 OutputStream os = socket.getOutputStream(); //3. 读取文件 FileInputStream fis = new FileInputStream(new File("kok-s0s.jpg")); //4. 写出文件 byte[] buffer = new byte[1024]; int len = 0; while ((len = fis.read(buffer)) != -1) { os.write(buffer, 0, len); } //通知服务器我已经结束了 socket.shutdownOutput();//传输已经完毕了 //确定服务器接受完毕，才能够断开连接 InputStream inputStream = socket.getInputStream(); //String byte[] ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer2 = new byte[1024]; int len2 = 0; while ((len2 = inputStream.read(buffer2)) != -1) { baos.write(buffer2, 0, len2); } System.out.println(baos.toString()); //5. 关闭资源 baos.close(); inputStream.close(); fis.close(); os.close(); socket.close(); } } Tomcat 服务端 自定义 S Tomcat服务器 S ：Java后台开发 客户端 自定义 C 浏览器 B UDP消息发送 发消息：无需连接，但需要知道对方的地址！ 发送端 package lesson03; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; //不需要连接服务器 public class UdpA { public static void main(String[] args) throws Exception { //1. 建立一个Socket DatagramSocket socket = new DatagramSocket(); //2. 建个包 String msg = "hello,服务器！"; InetAddress localhost = InetAddress.getByName("localhost"); int port = 9090; //数据，数据的长度 起始 要发送给谁 DatagramPacket packet = new DatagramPacket(msg.getBytes(), 0, msg.getBytes().length, localhost, port); //3. 发送包 socket.send(packet); //4. 关闭流 socket.close(); } } 接收端 package lesson03; import java.net.DatagramPacket; import java.net.DatagramSocket; //接受方 需要等待客户端的连接，eg：类似取快递，要有固定地址才能送货 public class UdpB { public static void main(String[] args) throws Exception { //开放端口 DatagramSocket socket = new DatagramSocket(9090); //接收数据包 byte[] buffer = new byte[1024]; DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length); socket.receive(packet); System.out.println(packet.getAddress().getHostAddress()); System.out.println(new String(packet.getData(), 0, packet.getLength())); //关闭连接 socket.close(); } } UDP聊天实现 发送方循环发送 package chat; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; public class UdpSender { public static void main(String[] args) throws Exception { DatagramSocket socket = new DatagramSocket(8888); //准备数据：控制台读取 BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); while (true) { String data = reader.readLine(); byte[] datas = data.getBytes(); DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, new InetSocketAddress("localhost", 6666)); socket.send(packet); if (data.equals("cool")) break; } socket.close(); } } 接受方循环接受 package chat; import java.net.DatagramPacket; import java.net.DatagramSocket; public class UdpReceiver { public static void main(String[] args) throws Exception { DatagramSocket socket = new DatagramSocket(6666); while (true) { //准备接受包裹 byte[] container = new byte[1024]; DatagramPacket packet = new DatagramPacket(container, 0, container.length); socket.receive(packet);//阻塞式接受包裹 //断开链接 byebye byte[] data = packet.getData(); String receiveData = new String(data, 0, packet.getLength()); System.out.println(receiveData); if (receiveData.equals("cool")) break; } socket.close(); } } UDP多线程在线聊天 线程Sender package chat; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; public class TalkSend implements Runnable { DatagramSocket socket = null; BufferedReader reader = null; private int formPort; private String toIP; private int toPort; public TalkSend(int formPort, String toIP, int toPort) { this.formPort = formPort; this.toIP = toIP; this.toPort = toPort; try { socket = new DatagramSocket(formPort); reader = new BufferedReader(new InputStreamReader(System.in)); } catch (Exception e) { e.printStackTrace(); } } @Override public void run() { while (true) { try { String data = reader.readLine(); byte[] datas = data.getBytes(); DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, new InetSocketAddress(this.toIP, this.toPort)); socket.send(packet); if (data.equals("bye")) break; } catch (Exception e) { e.printStackTrace(); } } socket.close(); } } 线程Receiver package chat; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; public class TalkReceive implements Runnable { DatagramSocket socket = null; private int port; private String msgFrom; public TalkReceive(int port, String msgFrom) { this.port = port; this.msgFrom = msgFrom; try { socket = new DatagramSocket(port); } catch (SocketException e) { e.printStackTrace(); } } @Override public void run() { while (true) { try { //准备接受包裹 byte[] container = new byte[1024]; DatagramPacket packet = new DatagramPacket(container, 0, container.length); socket.receive(packet);//阻塞式接受包裹 //断开链接 byebye byte[] data = packet.getData(); String receiveData = new String(data, 0, packet.getLength()); System.out.println(msgFrom + ":" + receiveData); if (receiveData.equals("bye")) break; } catch (Exception e) { e.printStackTrace(); } } socket.close(); } } girl package chat; public class TalkA { public static void main(String[] args) { new Thread(new TalkSend(7777, "localhost", 9999)).start(); new Thread(new TalkReceive(8888, "boy")).start(); } } boy package chat; public class TalkB { public static void main(String[] args) { new Thread(new TalkSend(5555, "localhost", 8888)).start(); new Thread(new TalkReceive(9999, "girl")).start(); } } URL下载网络资源 url：统一资源定位符，定位互联网上的某一个资源 DNS 域名解析 协议://ip地址:端口/项目名/资源 # 可以少但不能多 package lesson04; //http://localhost:8080/kok-s0s/hello.txt import java.net.MalformedURLException; import java.net.URL; public class URLDemo01 { public static void main(String[] args) throws MalformedURLException { URL url = new URL("http://localhost:8080/kok-s0s/index.jsp?username=kok-s0s&password=123"); System.out.println(url.getProtocol()); //协议 System.out.println(url.getHost()); //主机ip System.out.println(url.getPort()); //端口 System.out.println(url.getPath()); //文件 System.out.println(url.getFile()); //全路径 System.out.println(url.getQuery()); //参数 } } 下载音乐 package lesson04; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; public class UrlDown { public static void main(String[] args) throws IOException { //1. 下载地址 URL url = new URL("https://m701.music.126.net/20210330161559/0adf1c364da84a4b3b8d39adbbe7ee39/jdyyaac/obj/w5rDlsOJwrLDjj7CmsOj/7449465886/c45e/1cfa/63cc/e64fb7fc577f4ab0892b23ba6a7f6632.m4a"); //2. 连接到这个资源 HTTP HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); InputStream inputStream = urlConnection.getInputStream(); FileOutputStream fos = new FileOutputStream("ado.mp3"); byte[] buffer = new byte[1024]; int len; while ((len = inputStream.read(buffer)) != -1) { fos.write(buffer, 0, len); //写出这个资源 } fos.close(); inputStream.close(); urlConnection.disconnect(); //断开连接 } }
⭐Important 学习渠道_遇见狂神说B站视频 线程基本概念 线程 进程 多线程 多线程 多个同时在运行的任务 一个进程可以有多个线程，如视频中同时听声音，看图像，看弹幕，等等 Process与Thread 程序，是指令和数据的有序集合，其本身没有任何运行的含义，是一个静态的概念。 进程则是执行程序的一次执行过程，它是一个动态的概念。是系统资源分配的单位。 通常一个进程中可以包含若干个线程，当然一个进程中至少有一个线程，不然没有存在的意义。线程是CPU调度和执行的单位。 Tips：很多多线程是模拟出来的，真正的多线程是指有多个CPU，即多核，如服务器。如果是模拟出来的多线程，即在一个CPU的情况下，在同一个时间点，CPU只能执行一个代码，因为切换的很快，所以就有同时执行的错觉。 概念 线程就是独立的执行路径； 在程序运行时，即使没有自己创建线程，后台也会有很多个线程，如主线程，gc线程； main()称之为主线程，为系统的入口，用于执行整个程序； 在一个进程中，如果开辟了多个线程，线程的运行由调度器安排调度，调度器是与操作系统紧密相关的，先后顺序是不能人为干预的； 对同一份资源操作时，会存在资源抢夺的问题，需要加入并发控制； 线程会带来额外的开销，如CPU调度时间，并发控制开销； 每个线程在自己的工作内存交互，内存控制不当会造成数据不一致； 继承Thread类 Thread细节看Java SE 8帮助文档 package demo01; //创建线程方式一：继承Thread类，重写run()方法，调用start开启线程 //总结：注意，线程开启不一定立即执行，由CPU调度执行 public class TestThread extends Thread { @Override public void run() { //run方法线程体 for (int i = 0; i < 200; i++) { System.out.println("I'm watching you!" + i); } } public static void main(String[] args) { //main线程，主线程 //创建一个线程对象 TestThread testThread1 = new TestThread(); //调用start()方法开启线程 testThread1.start(); for (int i = 0; i < 1000; i++) { System.out.println("I'm learning!" + i); } } } 网图下载 package demo01; import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.net.URL; //练习Thread，实现多线程同步下载图片 public class TestThread2 extends Thread { private String url; //网络图片地址 private String name; //保存的文件名 public TestThread2(String url, String name) { this.url = url; this.name = name; } @Override public void run() { WebDownloader webDownloader = new WebDownloader(); webDownloader.downloader(url, name); System.out.println("下载了文件名为：" + name); } public static void main(String[] args) { TestThread2 t1 = new TestThread2("https://www.kok-s0s.top/usr/uploads/2021/03/750247936.jpg", "1.jpg"); TestThread2 t2 = new TestThread2("https://www.kok-s0s.top/usr/uploads/2021/03/1165126803.jpg", "2.jpg"); TestThread2 t3 = new TestThread2("https://www.kok-s0s.top/usr/uploads/2021/03/564529999.jpg", "3.jpg"); //线程并不一定立即执行，由CPU进行调度 t1.start(); t2.start(); t3.start(); } } //下载器 class WebDownloader { //下载方法 public void downloader(String url, String name) { try { FileUtils.copyURLToFile(new URL(url), new File(name)); } catch (IOException e) { e.printStackTrace(); System.out.println("IO异常，downloader方法出现问题"); } } } 实现Runnable接口 package demo01; //创建线程方式2：实现runnable接口，重写run()方法，执行线程需要丢入runnable接口实现类，调用start()方法 public class TestThread3 implements Runnable { @Override public void run() { //run方法线程体 for (int i = 0; i < 200; i++) { System.out.println("I'm watching you!" + i); } } public static void main(String[] args) { //创建runnable接口的实现类对象 TestThread3 testThread3 = new TestThread3(); //创建线程对象，通过线程对象来开启我们的线程，代理 //Thread thread = new Thread(testThread3); //thread.start(); new Thread(testThread3).start(); for (int i = 0; i < 1000; i++) { System.out.println("I'm learning!" + i); } } } 继承Thread类 子类继承Thread类具备多线程能力 启动线程：子类对象.start() 不建议使用：避免OOP单继承局限性 实现Runnable接口 实现接口Runnable具有多线程能力 启动线程：传入目标对象+Thread对象.start() 推荐使用：避免单继承局限性，灵活方便，方便同一个对象被多个线程使用 一份资源，多个代理 初识并发问题 package demo01; //多个线程同时操作同一个对象 //买火车票的例子 //发现问题：多个线程操作同一个资源的情况下，线程不安全，数据紊乱。 public class TestThread4 implements Runnable { //票数 private int ticketNums = 10; @Override public void run() { while (true) { if (ticketNums <= 0) { break; } //模拟延时 try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "-->拿到了第" + ticketNums-- + "张票"); } } public static void main(String[] args) { TestThread4 ticket = new TestThread4(); new Thread(ticket, "小明").start(); new Thread(ticket, "老师").start(); new Thread(ticket, "黄牛").start(); } } 龟兔赛跑 package demo01; //模拟龟兔赛跑 public class Race implements Runnable { //胜利者 private static String winner; @Override public void run() { for (int i = 0; i <= 100; i++) { //模拟兔子休息 if (Thread.currentThread().getName().equals("兔子") && i % 10 == 0) { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } //判断比赛是否结束 boolean flag = gameOver(i); //如果比赛结束，就停止程序 if (flag) { break; } System.out.println(Thread.currentThread().getName() + "-->跑了" + i + "步"); } } //判断是否完成比赛 private boolean gameOver(int steps) { //判断是否有胜利者 if (winner != null) { //已经存在胜利者 return true; } { if (steps >= 100) { winner = Thread.currentThread().getName(); System.out.println("winner is " + winner); return true; } } return false; } public static void main(String[] args) { Race race = new Race(); new Thread(race, "乌龟").start(); new Thread(race, "兔子").start(); } } 实现Callable接口 package demo02; //线程创建方式三：实现Callable接口 /* callable可以定义返回值，可以抛出异常 */ import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.concurrent.*; public class TestCallable implements Callable<Boolean> { private String url; //网络图片地址 private String name; //保存的文件名 public TestCallable(String url, String name) { this.url = url; this.name = name; } @Override public Boolean call() { WebDownloader webDownloader = new WebDownloader(); webDownloader.downloader(url, name); System.out.println("下载了文件名为：" + name); return true; } public static void main(String[] args) throws ExecutionException, InterruptedException { TestCallable t1 = new TestCallable("https://www.kok-s0s.top/usr/uploads/2021/03/750247936.jpg", "1.jpg"); TestCallable t2 = new TestCallable("https://www.kok-s0s.top/usr/uploads/2021/03/1165126803.jpg", "2.jpg"); TestCallable t3 = new TestCallable("https://www.kok-s0s.top/usr/uploads/2021/03/564529999.jpg", "3.jpg"); //线程并不一定立即执行，由CPU进行调度 //创建执行服务 ExecutorService ser = Executors.newFixedThreadPool(3); //提交执行 Future<Boolean> r1 = ser.submit(t1); Future<Boolean> r2 = ser.submit(t2); Future<Boolean> r3 = ser.submit(t3); //获取结果 boolean rs1 = r1.get(); boolean rs2 = r2.get(); boolean rs3 = r3.get(); System.out.println(rs1); System.out.println(rs2); System.out.println(rs3); //关闭服务 ser.shutdownNow(); } } //下载器 class WebDownloader { //下载方法 public void downloader(String url, String name) { try { FileUtils.copyURLToFile(new URL(url), new File(name)); } catch (IOException e) { e.printStackTrace(); System.out.println("IO异常，downloader方法出现问题"); } } } 静态代理模式 package demo03; //静态代理 //真实对象和代理对象都要实现同一个接口 //代理对象要代理真实角色 //好处： //代理对象可以做很多真实对象做不了的事情 //真实对象专注于自己的事情 public class StaticProxy { public static void main(String[] args) { You you = new You();//你要结婚 //匿名写法 new Thread(() -> System.out.println("i love you!")).start(); new WeddingCompamy(new You()).HappyMarry(); // WeddingCompamy weddingCompamy = new WeddingCompamy(you); // weddingCompamy.HappyMarry(); } } interface Marry { //结婚 void HappyMarry(); } //真实角色 ，你要结婚。 class You implements Marry { @Override public void HappyMarry() { System.out.println("你想结婚吗？"); } } //代理角色，帮助你结婚 class WeddingCompamy implements Marry { //代理谁-->真实目标角色 private Marry target; public WeddingCompamy(Marry target) { this.target = target; } @Override public void HappyMarry() { before(); this.target.HappyMarry();//这就是真实对象 after(); } private void after() { System.out.println("结婚后，收尾款！"); } private void before() { System.out.println("布置婚礼现场"); } } Lambda表达式 package demo04; /* 推导lambda表达式 */ public class TestLambda1 { //3.静态内部类 static class Like2 implements ILike { @Override public void lambda() { System.out.println("I like lambda2!"); } } public static void main(String[] args) { ILike like = new Like(); like.lambda(); like = new Like2(); like.lambda(); //4.局部内部类 class Like3 implements ILike { @Override public void lambda() { System.out.println("I like lambda3!"); } } like = new Like3(); like.lambda(); //5.匿名内部类，没有类的名称，必须借助接口或者父类 like = new ILike() { @Override public void lambda() { System.out.println("I like lambda4!"); } }; like.lambda(); //6.用lambda简化 like = () -> { System.out.println("I like lambda5!"); }; like.lambda(); } } //1.定义一个函数式接口 interface ILike { void lambda(); } //2.实现类 class Like implements ILike { @Override public void lambda() { System.out.println("I like lambda!"); } } package demo04; public class Testlambda2 { public static void main(String[] args) { // ILove love = null; //1.lambda表示简化 ILove love = (int a, int b) -> { System.out.println("she love you-->" + a); }; //简化1.去掉参数类型 love = (a, b) -> { System.out.println("he love you-->" + a); System.out.println(b + "不喜欢你！"); }; //简化2.简化括号 // love = a -> { // System.out.println("do you love " + a); // System.out.println("但她不喜欢你了"); // }; //简化3.去掉花括号 //love = a -> System.out.println("i reall love " + a); love.love(40, 11); //总结： //lambda表达式只能有一行代码的情况下次才能简化成为一行，如果有多行，需要用代码块包裹 //前提接口是函数式接口（接口里只能有一个方法） //多个参数也可以去掉参数类型，要去掉就全都去掉了，必须加上括号 } } interface ILove { void love(int a, int b); } 线程五大状态 停止线程 不推荐使用JDK提供的stop()，destroy()方法。【已废弃】 推荐让线程自己停止下来 建议使用一个标志位进行终止变量 当flag==false，则终止线程运行。 package state; //测试stop //1.建议线程正常停止-->利用次数，不建议死循环 //2.建议使用标志位-->设置一个标志位 //3.不要使用stop或者destroy等过时或者JDK不建议使用的方法 public class TestStop implements Runnable { //设置一个标志位 private boolean flag = true; @Override public void run() { int i = 0; while (flag) { System.out.println("run...Thread!" + i++); } } //2.设置一个公开的方法停止线程，转换标志位 public void stop() { this.flag = false; } public static void main(String[] args) { TestStop testStop = new TestStop(); new Thread(testStop).start(); for (int i = 0; i < 1000; i++) { System.out.println("main线程运行中 " + i); if (i == 900) { //调用stop方法转换标志位，让线程停止 testStop.stop(); System.out.println("线程停止了"); } } } } 线程休眠——sleep sleep（时间）指定当前线程阻塞的毫秒数； sleep存在异常InterruptedException； sleep时间达到后线程进入就绪状态； sleep可以模拟网络延时，倒计时等。 每一个对象都有一个锁，sleep不会释放锁； package state; //模拟网络延时 作用：放大问题的发生性 public class TestSleep implements Runnable { //票数 private int ticketNums = 10; @Override public void run() { while (true) { if (ticketNums <= 0) { break; } //模拟延时 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "-->拿到了第" + ticketNums-- + "张票"); } } public static void main(String[] args) { TestSleep ticket = new TestSleep(); new Thread(ticket, "小明").start(); new Thread(ticket, "老师").start(); new Thread(ticket, "黄牛").start(); } } package state; import java.text.SimpleDateFormat; import java.util.Date; //模拟倒计时... public class TestSleep2 { public static void main(String[] args) { // try { // tenDown(); // } catch (InterruptedException e) { // e.printStackTrace(); // } //打印当前系统时间 Date startTime = new Date(System.currentTimeMillis());//获取系统当前时间 while (true) { try { Thread.sleep(1000); System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime)); startTime = new Date(System.currentTimeMillis());//更新当前时间 } catch (InterruptedException e) { e.printStackTrace(); } } } //模拟倒计时 public static void tenDown() throws InterruptedException { int num = 10; while (true) { Thread.sleep(1000); System.out.println(num--); if (num <= 0) { break; } } } } 线程礼让 礼让线程，让当前正在执行的线程暂停，但不阻塞 将线程从运行状态转为就绪状态 让CPU重新调度，礼让不一定成功！看CPU心情 package state; //测试礼让线程 // 礼让不一定成功，看CPU心情 public class TestYied { public static void main(String[] args) { MyYied myYied = new MyYied(); new Thread(myYied, "a").start(); new Thread(myYied, "b").start(); } } class MyYied implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() + "线程开始执行"); Thread.yield();//礼让 System.out.println(Thread.currentThread().getName() + "线程停止执行"); } } 线程强制执行__join Join合并线程，待此线程执行完成后，再执行其它线程，其它线程阻塞 类比于插队 package state; //测试join方法 类比于插队 public class TestJoin implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("线程VIP来了" + i); } } public static void main(String[] args) throws InterruptedException { //启动我们的线程 TestJoin testJoin = new TestJoin(); Thread thread = new Thread(testJoin); thread.start(); //主线程 for (int i = 0; i < 50; i++) { if (i == 10) { thread.join();//插队 } System.out.println("main" + i); } } } 观测线程状态 Thread.State 线程状态。线程可以处于以下状态之一： NEW 尚未启动的线程出于此状态 RUNNABLE 在Java虚拟机中执行的线程出于此状态 BLOCKED 被阻塞等待监视器锁定的线程出于此状态 WAITING 正在等待另一个线程执行特定动作的线程出于此状态 TIMED_WAITING 正在等待另一个线程执行动作达到执行等待时间的线程出于此状态 TERMINATED 已退出的线程处于此状态 一个线程可以在给的那个时间点处于一个状态。这些状态是不反映任何操作系统线程状态的虚拟机状态。 package state; //观察测试线程的状态 public class TestState { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(() -> { for (int i = 0; i < 5; i++) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("////////"); }); //观察状态 Thread.State state = thread.getState(); System.out.println(state); //观察启动后 thread.start();//启动线程 state = thread.getState(); System.out.println(state); //Run while (state != Thread.State.TERMINATED) { //只要线程不终止，就一直输出状态 Thread.sleep(100); state = thread.getState();//更新线程状态 System.out.println(state);//输出状态 } // thread.start(); //死亡的线程无法再次启动 } } 线程的优先级 Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程，线程调度器按照优先级决定应该调度哪个线程来执行。 线程的优先级用数字表示，范围从1~10 Thread.MIN_PRIORITY = 1; Thread.MAX_PRIORITY = 10; Thread.NORM_PRIORITY = 5; 使用以下方式改变或获取优先级 getPriority() setPriority(int xxx) 优先级的设定建议在start()调度前 优先级低只是意味着获得调度的概率低，并不是优先级低就不会被调用了，主要看CPU的调度 question：性能倒置 package state; //测试线程的优先级 public class TestPriority { public static void main(String[] args) { //主线程默认优先级 System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority()); MyPriority myPriority = new MyPriority(); Thread t1 = new Thread(myPriority); Thread t2 = new Thread(myPriority); Thread t3 = new Thread(myPriority); Thread t4 = new Thread(myPriority); Thread t5 = new Thread(myPriority); Thread t6 = new Thread(myPriority); //先设置优先级，再启动 t1.start(); t2.setPriority(1); t2.start(); t3.setPriority(4); t3.start(); t4.setPriority(Thread.MAX_PRIORITY); t4.start(); t5.setPriority(8); t5.start(); t6.setPriority(7); t6.start(); } } class MyPriority implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority()); } } 守护（daemon）线程 线程分为用户线程和守护线程 虚拟机必须确保用户线程（如main线程）执行完毕 虚拟机不用等待守护线程执行完毕 如：后台记录操作日志，监控内存，垃圾回收等待··· package state; //测试守护线程 //eg:上帝守护你 public class TestDaemon { public static void main(String[] args) { God god = new God(); You you = new You(); Thread thread = new Thread(god); thread.setDaemon(true); //默认是false表示是用户线程，正常的线程都是用户线程 thread.start();//上帝守护线程启动 new Thread(you).start(); //你 用户线程启动··· } } //上帝 class God implements Runnable { @Override public void run() { while (true) { System.out.println("上帝保护你"); } } } //你 class You implements Runnable { @Override public void run() { for (int i = 0; i < 35600; i++) { System.out.println("一生开心的活着！"); } System.out.println("-==========say goodbye!"); } } 线程同步 线程同步机制 并发：同一个对象被多个线程同时操作 队列 锁 eg：景区厕所排队（厕所可以关起来不让其他人进入） 线程同步 由于同一进程的多个线程共享一块存储空间，在带来方便的同时，也带来了访问冲突问题，为了保证数据在方法中被访问时的正确性，在访问时加入锁机制synchronized，当一个线程获得对象的排它锁，独占资源，其它线程必须等待，使用后释放锁即可，存在以下问题： 一个线程持有锁会导致其它所有需要此锁的线程挂起； 在多线程竞争下，加锁，释放锁会导致比较多的上下文切换和调度延时，引起性能问题； 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置，引起性能问题； 三大不安全案列 买票不安全 package syn; //不安全的买票 //线程不安全，有负数 public class UnsafeBuyTicket { public static void main(String[] args) { BuyTicket station = new BuyTicket(); new Thread(station, "小明").start(); new Thread(station, "黄牛").start(); new Thread(station, "teacher").start(); } } class BuyTicket implements Runnable { boolean flag = true;//外部停止方式 //票 private int ticketNums = 10; @Override public void run() { //买票 while (flag) { try { buy(); } catch (Exception e) { e.printStackTrace(); } } } private void buy() { //判断是否有票 if (ticketNums <= 0) { return; } //sleep可以放大问题的发生性 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } //买票 System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--); } } 取钱不安全 package syn; //不安全的取钱 //两个人去银行取钱（需要账户） public class UnsafeBank { public static void main(String[] args) { //账户 Account account = new Account(100, "零用钱"); Drawing you = new Drawing(account, 100, "you"); Drawing girlFriend = new Drawing(account, 100, "girlFriend"); you.start(); girlFriend.start(); } } //账户 class Account { int money;//余额 String name;//卡名 public Account(int money, String name) { this.money = money; this.name = name; } } //银行：模拟取款 class Drawing extends Thread { //继承Thread类，可以使用该类中的方法 Account account;//账户 int drawingMoney;//去了多少钱 int nowMoney; //现在手里有多少钱 public Drawing(Account account, int drawingMoney, String name) { super(name); this.account = account; this.drawingMoney = drawingMoney; } //取钱 @Override public void run() { //判断是否还有钱 if (account.money - drawingMoney < 0) { System.out.println(Thread.currentThread().getName() + "钱不够，无法取钱！"); return; } //sleep可以放大问题的发生性 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //卡内余额 = 余额 - 你取的钱 account.money = account.money - drawingMoney; //你手里的钱 nowMoney = nowMoney + drawingMoney; System.out.println(account.name + "余额为" + account.money); // Thread.currentThread().getName() = this.getName() System.out.println(this.getName() + "手里的钱为" + nowMoney); } } 线程不安全 package syn; import java.util.ArrayList; import java.util.List; //线程不安全的集合 public class UnsafeList { public static void main(String[] args) { List<String> list = new ArrayList<String>(); for (int i = 0; i < 10000; i++) { new Thread(() -> { list.add(Thread.currentThread().getName()); //可能会存在有线程覆盖了，导致元素减少 }).start(); } try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size()); } } 同步方法及同步块 安全买票 package syn; //不安全的买票 //线程不安全，有负数 public class UnsafeBuyTicket { public static void main(String[] args) { BuyTicket station = new BuyTicket(); new Thread(station, "小明").start(); new Thread(station, "黄牛").start(); new Thread(station, "teacher").start(); } } class BuyTicket implements Runnable { boolean flag = true;//外部停止方式 //票 private int ticketNums = 10; @Override public void run() { //买票 while (flag) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } try { buy(); } catch (Exception e) { e.printStackTrace(); } } } //synchronized 同步方法，锁的是this private synchronized void buy() { //判断是否有票 if (ticketNums <= 0) { return; } //sleep可以放大问题的发生性 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } //买票 System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--); } } 安全取钱 package syn; //不安全的取钱 //两个人去银行取钱（需要账户） public class UnsafeBank { public static void main(String[] args) { //账户 Account account = new Account(10000, "零用钱"); Drawing you = new Drawing(account, 100, "you"); Drawing girlFriend = new Drawing(account, 100, "girlFriend"); you.start(); girlFriend.start(); } } //账户 class Account { int money;//余额 String name;//卡名 public Account(int money, String name) { this.money = money; this.name = name; } } //银行：模拟取款 class Drawing extends Thread { //继承Thread类，可以使用该类中的方法 Account account;//账户 int drawingMoney;//去了多少钱 int nowMoney; //现在手里有多少钱 public Drawing(Account account, int drawingMoney, String name) { super(name); this.account = account; this.drawingMoney = drawingMoney; } //synchronized 同步方法 默认锁的是this它本身 //取钱 @Override public void run() { //锁的对象是变化的量 需要增删改的 //synchronized 同步块可以锁任何对象 synchronized (account) { //判断是否还有钱 if (account.money - drawingMoney < 0) { System.out.println(Thread.currentThread().getName() + "钱不够，无法取钱！"); return; } //sleep可以放大问题的发生性 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //卡内余额 = 余额 - 你取的钱 account.money = account.money - drawingMoney; //你手里的钱 nowMoney = nowMoney + drawingMoney; System.out.println(account.name + "余额为" + account.money); // Thread.currentThread().getName() = this.getName() System.out.println(this.getName() + "手里的钱为" + nowMoney); } } } 线程安全 package syn; import java.util.ArrayList; import java.util.List; //线程不安全的集合 public class UnsafeList { public static void main(String[] args) { List<String> list = new ArrayList<String>(); for (int i = 0; i < 10000; i++) { new Thread(() -> { synchronized (list){ list.add(Thread.currentThread().getName()); } // list.add(Thread.currentThread().getName()); //可能会存在有线程覆盖了，导致元素减少 }).start(); } try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size()); } } CopyOnWriteArrayList package syn; import java.util.concurrent.CopyOnWriteArrayList; //测试JUC安全类型的集合 安全的 public class TestJUC { public static void main(String[] args) { CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>(); for (int i = 0; i < 10000; i++) { new Thread(() -> { list.add(Thread.currentThread().getName()); }).start(); } try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size()); } } 死锁 多个线程各自占有一些共享资源，并且互相等待其它线程占有的资源才能运行，而导致两个或者多个线程都在等待对方释放资源，都停止执行的情况。某一个同步块同时拥有"两个以上对象的锁"时，就可能发生“死锁”的问题。 产生死锁的四个必要条件： 互斥条件：一个资源每次只能被一个进程使用。 请求与保持条件：一个进程因请求资源而阻塞时，对已获得的资源保持不放。 不剥夺条件：进程已获得的资源，在未使用完之前，不能强行剥夺。 循环等待条件：若干进程之间形成一种头尾相接的循环等待资源关系。 死锁避免方法：只需要破坏上述四个条件中的任意一个或多个条件就可以避免死锁发生。 package thread; //死锁：多个线程互相抱着对方需要的资源，然后形成僵持 public class DeadLock { public static void main(String[] args) { Makeup g1 = new Makeup(0, "灰姑娘"); Makeup g2 = new Makeup(1, "白雪公主"); g1.start(); g2.start(); } } //口红 class Lipstick { } //镜子 class Mirror { } class Makeup extends Thread { //需要的资源只有一份，用static来保证只有一份 static Lipstick lipstick = new Lipstick(); static Mirror mirror = new Mirror(); int choice;//选择 String girlName; //使用化妆品的人 Makeup(int choice, String girlName) { this.choice = choice; this.girlName = girlName; } @Override public void run() { //化妆 try { makeup(); } catch (Exception e) { e.printStackTrace(); } } //化妆，互相持有对方的锁，就是需要拿到对方的资源 private void makeup() { if (choice == 0) { synchronized (lipstick) {//获得口红的锁 System.out.println(this.girlName + "获得口红的锁"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } // synchronized (mirror) {//一秒钟后想获得镜子 // System.out.println(this.girlName + "获得镜子的锁"); // } } synchronized (mirror) {//一秒钟后想获得镜子 System.out.println(this.girlName + "获得镜子的锁"); } } else { synchronized (mirror) {//获得镜子的锁 System.out.println(this.girlName + "获得镜子的锁"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } // synchronized (lipstick) {//一秒钟后想获得口红 // System.out.println(this.girlName + "获得口红的锁"); // } } synchronized (lipstick) {//一秒钟后想获得口红 System.out.println(this.girlName + "获得口红的锁"); } } } } Lock锁 从JDK5.0开始，Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当。 java.util.concurrent.locks.Lock接口时控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问，每次只能有一个线程对Lock对象加锁，线程开始访问共享资源之前应先获得Lock对象。 ReentrantLock可重入锁类实现了Lock，它拥有与synchronized相同的并发性和内存语义，在实现线程安全的控制中，比较常用的是ReentrantLock，可以显式加锁，释放锁。 package learn_lock; import java.util.concurrent.locks.ReentrantLock; public class TestLock { public static void main(String[] args) { TeskLock2 teskLock2 = new TeskLock2(); new Thread(teskLock2).start(); new Thread(teskLock2).start(); new Thread(teskLock2).start(); } } class TeskLock2 implements Runnable { //定义lock锁 private final ReentrantLock lock = new ReentrantLock(); int ticketNums = 10; @Override public void run() { while (true) { try { lock.lock();//加锁 if (ticketNums > 0) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(ticketNums--); } else { break; } } finally { //解锁 lock.unlock(); } } } } 一般使用样例 class A { private final ReentrantLock lock = new ReentrantLock(); public void m() { try { lock.lock(); //保证线程安全的代码 } finally { lock.unlock(); //如果同步代码有异常，要将unlock()写入finally语句块 } } } synchronized与Lock的对比 Lock是显式锁（手动开启和关闭锁，不能忘记关闭锁），synchronized是隐式锁，出了作用域自动释放 Lock只有代码块锁，synchronized有代码块锁和方法锁 使用Lock锁，JVM将花费较少的时间来调度线程，性能更好。并且具有更好的扩展性（提供更多的子类） 优先使用顺序： Lock > 同步代码块（已经进入了方法体，分配了相应资源）> 同步方法（在方法体之外） 线程通信 生产者消费者问题 video 管程法 并发协作模型 “生产者/消费者模式”--->管程法 生产者：负责生产数据的模块（可能是方法，对象，线程，进程）； 消费者：负责处理数据的模块（可能是方法，对象，线程，进程）； 缓冲区：消费者不能直接使用生产者的数据，它们之间有个“缓冲区” 生产者将生产好的数据放入缓冲区，消费者从缓冲区拿出数据。 package learn_lock; //生产者消费者模型-->利用缓冲区解决：管程法 //生产者，消费者，产品，缓冲区 public class TestPC { public static void main(String[] args) { SynContainer container = new SynContainer(); new Productor(container).start(); new Consumer(container).start(); } } //生产者 class Productor extends Thread { SynContainer container; public Productor(SynContainer container) { this.container = container; } //生产 @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("生产了" + i + "只鸡"); container.push(new Chicken(i)); } } } //消费者 class Consumer extends Thread { SynContainer container; public Consumer(SynContainer container) { this.container = container; } //消费 @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("消费了-->" + container.pop().id + "只鸡"); } } } //产品 class Chicken { int id;//产品编号 public Chicken(int id) { this.id = id; } } //缓冲区 class SynContainer { //需要一个容器大小 Chicken[] chickens = new Chicken[10]; //容器计数器 int count = 0; //生产者放入产品 public synchronized void push(Chicken chicken) { //如果容器满了，就需要等待消费者消费 if (count == chickens.length) { //通知消费者消费，生产等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //如果没有满，我们就需要丢入产品 chickens[count] = chicken; count++; //可以通知消费者消费了 this.notifyAll(); } //消费者消费产品 public synchronized Chicken pop() { //判断能否消费 if (count == 0) { //等待生产者生产，消费者等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //如果可以消费 count--; Chicken chicken = chickens[count]; //吃完了，通知生产者生产 this.notifyAll(); return chicken; } } 信号灯法 并发协作模型 “生产者/消费者模式”--->信号灯法 eg：红绿灯 package learn_lock; //测试生产者消费者问题2：信号灯法，标志位flag解决 public class TestPC2 { public static void main(String[] args) { TV tv = new TV(); new Player(tv).start(); new Watcher(tv).start(); } } //生产者--->演员 class Player extends Thread { TV tv; public Player(TV tv) { this.tv = tv; } @Override public void run() { for (int i = 0; i < 20; i++) { if (i % 2 == 0) { this.tv.play("快乐大本营播放中"); } else { this.tv.play("video：tv shows"); } } } } //消费者--->观众 class Watcher extends Thread { TV tv; public Watcher(TV tv) { this.tv = tv; } @Override public void run() { for (int i = 0; i < 20; i++) { tv.watch(); } } } //产品--->节目 class TV { //演员表演，观众等待 T //观众观看，演员等待 F String voice;//表演的节目 boolean flag = true; //表演 public synchronized void play(String voice) { if (!flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("演员表演了：" + voice); //通知观众观看 this.notifyAll();//通知唤醒 this.voice = voice; this.flag = !this.flag; } //观看 public synchronized void watch() { if (flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("watcher观看了：" + voice); //通知演员表演 this.notifyAll(); this.flag = !this.flag; } } 线程池 使用线程池 背景：经常创建和销毁，使用量特别大的资源，比如并发情况下的线程，对性能影响很大。 思路：提前创建好多个线程，放入线程池中，使用时直接获取，使用完放回池中。可以避免频繁创建销毁，实现重复利用。类似生活中的公共交通工具。 好处： 提高响应速度（减少了创建新线程的时间） 降低资源消耗（重复利用线程池中线程，不需要每次都创建） 便于线程管理（···） corePoolSize：核心池的大小 maximumPoolSize：最大线程数 keepAliveTime：线程没有任务时最多保持多长时间后会终止 JDK5.0起提供了线程池相关API：ExecutorService和Executors ExecutorService：真正的线程池接口。常见子类ThreadPoolExecutor void executr(Runnable command)：执行任务/命令，没有返回值，一般用来执行Runnable <T> Future<T> submit(Callable<T> task)：执行任务，有返回值，一般用来执行Callable void shutdown()：关闭连接池 Executors：工具类，线程池的工厂类，用于创建并返回不同类型的线程池 package learn_lock; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; //测试线程池 public class TestPool { public static void main(String[] args) { //1.创建服务，创建线程池 //newFixedThreadPool 参数为：线程池大小 ExecutorService service = Executors.newFixedThreadPool(10); service.execute(new MyThread()); service.execute(new MyThread()); service.execute(new MyThread()); service.execute(new MyThread()); service.execute(new MyThread()); //2.关闭连接 service.shutdown(); } } class MyThread implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName()); } } 总结 package thread; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; //回顾总结线程的创建 public class ThreadNew { public static void main(String[] args) { new MyThread1().start(); new Thread(new MyThread2()).start(); FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3()); new Thread(futureTask).start(); try { Integer integer = futureTask.get(); System.out.println(integer); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } //1.继承Thread类 class MyThread1 extends Thread { @Override public void run() { System.out.println("MyThread1"); } } //2.实现Runnable接口 class MyThread2 implements Runnable { @Override public void run() { System.out.println("MyThread2"); } } //3.实现Callable接口 class MyThread3 implements Callable<Integer> { @Override public Integer call() throws Exception { System.out.println("MyThread3"); return 100; } } 未完待续···
卸载JDK 1.删除java的安装目录 2.清除变量设置里的JAVA_HOME 3.清除path与java相关的信息 4.试试java -version判断是否是否删除干净。 IDE 1.psvm自动生成主函数语句 2.sout快捷键自动生成输出语句 3.ctrl+alt+L能够做自动排版工作 4.Ctrl + D 复制当前行到下一行 intellij java 反编译 打开intellij的project struck选项,找到字节码文件的位置，将相应的文件拖到存放对应class文件的地方，再用intellij打开其文件，IDEA会自动反编译文件出来。 原文件 package struct; public class switchDemo { public static void main(String[] args) { String name = "kok-s0s"; //JDK7的新特性，表达式结果可以是字符串！！！ //字符的本质是数字 //反编译 java---class（字节码文件）---反编译（IDEA） switch (name) { case "kok-s0s": System.out.println("ok!"); break; case "who": System.out.println("who are you?"); break; default: System.out.println("what are you going to do?"); } } } 反编译结果 // // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package struct; public class switchDemo { public switchDemo() { } public static void main(String[] args) { String name = "kok-s0s"; byte var3 = -1; switch(name.hashCode()) { case -543248720: if (name.equals("kok-s0s")) { var3 = 0; } break; case 117694: if (name.equals("who")) { var3 = 1; } } switch(var3) { case 0: System.out.println("ok!"); break; case 1: System.out.println("who are you?"); break; default: System.out.println("what are you going to do?"); } } }