java笔记08
多线程&JUC
1.什么是多线程
1.什么是多线程?有了多线程,我们就可以让程序同时做多件事情 2.多线程的作用?提高效率 3.多线程的应用场景?只要你想让多个事情同时运行就需要用到多线程比如:软件中的耗时操作、所有的聊天软件、所有的服务器
2.并发和并行
并发:在同一时刻,有多个指令在单个CPU上交替执行 并行:在同一时刻,有多个指令在多个CPU上同时执行
3.多线程的实现方式
第一种实现方式:继承Thread类的方式进行实现
package a03_多线程的实现方式.a01_第一种实现方式;public class MyThread extends Thread {@Overridepublic void run() {//书写线程要执行代码for (int i = 0; i < 100; i++) {System.out.println(getName() + "HelloWorld");}} }
package a03_多线程的实现方式.a01_第一种实现方式;public class 继承Thread类的方式进行实现 {public static void main(String[] args) {/*多线程的第一种启动方式:继承Thread类的方式进行实现1. 自己定义一个类继承Thread2. 重写run方法3. 创建子类的对象,并启动线程*/MyThread t1 = new MyThread();MyThread t2 = new MyThread();t1.setName("线程1");t2.setName("线程2");t1.start();t2.start();} }
第二种实现方式:实现Runnable接口的方式进行实现
package a03_多线程的实现方式.a02_第二种实现方式;public class MyRun implements Runnable {@Overridepublic void run() {//书写线程要执行的代码for (int i = 0; i < 100; i++) {//获取到当前线程的对象/*Thread t = Thread.currentThread();System.out.println(t.getName() + "HelloWorld!");/*/System.out.println(Thread.currentThread().getName() + "HelloWorld!");}} }
package a03_多线程的实现方式.a02_第二种实现方式;public class 实现Runnable接口的方式进行实现 {public static void main(String[] args) {/** 多线程的第二种启动方式:* 1.自己定义一个类实现Runnable接口* 2.重写里面的run方法* 3.创建自己的类的对象* 4.创建一个Thread类的对象,并开启线程* *///创建MyRun的对象//表示多线程要执行的任务MyRun mr = new MyRun();//创建线程对象Thread t1 = new Thread(mr);Thread t2 = new Thread(mr);//给线程设置名字t1.setName("线程1");t2.setName("线程2");//开启线程t1.start();t2.start();} }
第三种实现方式:利用Callable接口和Future接口方式实现
package a03_多线程的实现方式.a03_第三种实现方式;import java.util.concurrent.Callable;public class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {//求1~100之间的和int sum = 0;for (int i = 1; i <= 100; i++) {sum = sum + i;}return sum;} }
package a03_多线程的实现方式.a03_第三种实现方式;import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask;public class 利用Callable接口和Future接口方式实现 {public static void main(String[] args) throws ExecutionException, InterruptedException {/*多线程的第三种实现方式:特点:可以获取到多线程运行的结果1. 创建一个类MyCallable实现Callable接口2. 重写call(是有返回值的,表示多线程运行的结果)3. 创建MyCallable的对象(表示多线程要执行的任务)4. 创建FutureTask的对象(作用:管理多线程运行的结果)5. 创建Thread类的对象,并启动(表示线程)*///创建MyCallable的对象(表示多线程要执行的任务)MyCallable mc = new MyCallable();//创建FutureTask的对象(作用:管理多线程运行的结果)FutureTask<Integer> ft = new FutureTask<>(mc);//创建线程的对象Thread t1 = new Thread(ft);//启动线程t1.start();//获取多线程运行的结果Integer result = ft.get();System.out.println(result);} }
多线程三种实现方式对比优点 缺点 继承Thread类 编程比较简单,可以直接使用Thread类中的方法 可扩展性较差,不能再继承其他的类 实现Runnable接口 扩展性强,实现该接口的同时还可 编程相对复杂,不能直接使用 实现Callable接口 以继承其他的类 Thread类中的方法
4.多线程中常见的成员方法
常用的成员方法
常见的成员方法 方法名称 说明 String getName() 返回此线程的名称 void setName(String name) 设置线程的名字(构造方法也可以设置名字)细节:1.如果我们没有给线程设置名字,线程也是有默认的名字的格式:Thread-X(X 序号,从 0 开始的)2、如果我们要给线程设置名字,可以用 set 方法进行设置,也可以构造方法设置static Thread currentThread() 获取当前线程的对象细节:当 JVM 虚拟机启动之后,会自动的启动多条线程其中有一条线程就叫做 main 线程他的作用就是去调用 main 方法,并执行里面的代码在以前,我们写的所有的代码,其实都是运行在 main 线程当中static void sleep(long time) 让线程休眠指定的时间,单位为毫秒细节:1.哪条线程执行到这个方法,那么哪条线程就会在这里停留对应的时间2.方法的参数:就表示睡眠的时间,单位毫秒1 秒 = 1000 毫秒3.当时间到了之后,线程会自动的醒来,继续执行下面的其他代码setPriority(int newPriority) 设置线程的优先级 final int getPriority() 获取线程的优先级 final void setDaemon(boolean on) 设置为守护线程 public static void yield() 出让线程 / 礼让线程 public static void join() 插入线程 / 插队线程
package a04_多线程中常见的成员方法;public class MyThread extends Thread {public MyThread() {}public MyThread(String name) {super(name);}@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(getName() + "@" + i);}} }
package a04_多线程中常见的成员方法;public class a01_常用的成员方法 {public static void main(String[] args) throws InterruptedException {//1.创建线程的对象MyThread t1 = new MyThread("飞机");MyThread t2 = new MyThread("坦克");//2.开启线程t1.start();t2.start();//哪条线程执行到这个方法,此时获取的就是哪条线程的对象Thread t = Thread.currentThread();String name = t.getName();System.out.println(name);//mainSystem.out.println("11111111111");Thread.sleep(5000);System.out.println("22222222222");} }
线程的优先级
抢占式调度:随机 方法名称 说明 setPriority(int newPriority) 设置线程的优先级 final int getPriority() 获取线程的优先级
package a04_多线程中常见的成员方法.a02_线程的优先级;public class MyRunnable implements Runnable{@Overridepublic void run() {for (int i = 1; i <= 100; i++) {System.out.println(Thread.currentThread().getName() + "---" + i);}} }
package a04_多线程中常见的成员方法.a02_线程的优先级;public class Test {public static void main(String[] args) {//创建线程要执行的参数对象MyRunnable mr = new MyRunnable();//创建线程对象Thread t1 = new Thread(mr, "飞机");Thread t2 = new Thread(mr, "坦克");//优先级默认为5t1.setPriority(1);t2.setPriority(10);t1.start();t2.start();} }
守护线程
final void setDaemon(boolean on) 设置为守护线程
package a04_多线程中常见的成员方法.a03_守护线程;public class MyThread1 extends Thread{@Overridepublic void run() {for (int i = 1; i <= 10; i++) {System.out.println(getName() + "@" + i);}} }
package a04_多线程中常见的成员方法.a03_守护线程;public class MyThread2 extends Thread{@Overridepublic void run() {for (int i = 1; i <= 100; i++) {System.out.println(getName() + "@" + i);}} }
package a04_多线程中常见的成员方法.a03_守护线程;public class Test {public static void main(String[] args) {/*final void setDaemon(boolean on) 设置为守护线程细节:当其他的非守护线程执行完毕之后,守护线程会陆续结束通俗易懂:当女神线程结束了,那么备胎也没有存在的必要了*/MyThread1 t1 = new MyThread1();MyThread2 t2 = new MyThread2();t1.setName("女神");t2.setName("备胎");//把第二个线程设置为守护线程(备胎线程)t2.setDaemon(true);t1.start();t2.start();} }
礼让线程
package a04_多线程中常见的成员方法.a04_礼让线程;public class MyThread extends Thread{@Overridepublic void run() {for (int i = 1; i <= 100; i++) {System.out.println(getName() + "@" + i);//表示出让当前CPU的执行权Thread.yield();}} }
package a04_多线程中常见的成员方法.a04_礼让线程;public class Test {public static void main(String[] args) {/*public static void yield() 出让线程/礼让线程*/MyThread t1 = new MyThread();MyThread t2 = new MyThread();t1.setName("飞机");t2.setName("坦克");t1.start();t2.start();} }
插入线程
package a04_多线程中常见的成员方法.a05_插入线程;public class MyThread extends Thread{@Overridepublic void run() {for (int i = 1; i <= 100; i++) {System.out.println(getName() + "@" + i);}} }
package a04_多线程中常见的成员方法.a05_插入线程;public class Test {public static void main(String[] args) throws InterruptedException {/*public final void join() 插入线程/插队线程*/MyThread t = new MyThread();t.setName("土豆");t.start();//表示把t这个线程,插入到当前线程之前。//t:土豆//当前线程: main线程t.join();//执行在main线程当中的for (int i = 0; i < 10; i++) {System.out.println("main线程" + i);}} }
5.线程的生命周期
线程的生命周期 新建:创建线程对象 就绪:有执行资格,没有执行权,不停的抢 CPU ,通过 start() 从新建进入就绪 运行:有执行资格,有执行权,运行代码 ,抢到 CPU 的执行权从就绪进入运行,其他线程抢走 CPU 的执行权从运行回到就绪 阻塞:没有执行资格,没有执行权,等着 ,通过 sleep() 或者其他阻塞式方法从运行进入阻塞,sleep() 方法时间到或其他阻塞方式结束从阻塞进入就绪 死亡:run() 执行完毕,线程死亡,变成垃圾
6.线程的安全问题
package a06_线程的安全问题;public class MyThread extends Thread {//表示这个类所有的对象,都共享ticket数据static int ticket = 0;//0 ~ 99@Overridepublic void run() {while (true) {if (ticket < 100) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}ticket++;System.out.println(getName() + "正在卖第" + ticket + "张票!!!");} else {break;}}} }
package a06_线程的安全问题;public class Test {public static void main(String[] args) { // 需求:某电影院目前正在上映国产大片,共有 100 张票,而它有 3 个窗口卖票,请设计一个程序模拟该电影院卖票//创建线程对象MyThread t1 = new MyThread();MyThread t2 = new MyThread();MyThread t3 = new MyThread();//起名字t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");//开启线程t1.start();t2.start();t3.start();} }
7.同步代码块
同步代码块:把操作共享数据的代码锁起来 格式: synchronized (锁){操作共享数据的代码 }特点 1:锁默认打开,有一个线程进去了,锁自动关闭 特点 2:里面的代码全部执行完毕,线程出来,锁自动打开
package a07_同步代码块;public class MyThread extends Thread {//表示这个类所有的对象,都共享ticket数据static int ticket = 0;//0 ~ 99//锁对象,一定要是唯一的 一般写MyThread.classstatic Object obj = new Object();@Overridepublic void run() {while (true) { // 同步代码块:把操作共享数据的代码锁起来 // 格式: // synchronized (锁){ // 操作共享数据的代码 // }// 特点 1:锁默认打开,有一个线程进去了,锁自动关闭 // 特点 2:里面的代码全部执行完毕,线程出来,锁自动打开synchronized (obj) {if (ticket < 100) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}ticket++;System.out.println(getName() + "正在卖第" + ticket + "张票!!!");} else {break;}}}} }
package a07_同步代码块;import a06_线程的安全问题.MyThread;public class Test {public static void main(String[] args) { // 需求:某电影院目前正在上映国产大片,共有 100 张票,而它有 3 个窗口卖票,请设计一个程序模拟该电影院卖票//创建线程对象a06_线程的安全问题.MyThread t1 = new a06_线程的安全问题.MyThread();a06_线程的安全问题.MyThread t2 = new a06_线程的安全问题.MyThread();a06_线程的安全问题.MyThread t3 = new MyThread();//起名字t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");//开启线程t1.start();t2.start();t3.start();} }
8.同步方法
package a08_同步方法;public class MyRunnable implements Runnable {int ticket = 0;@Overridepublic void run() {//1.循环while (true) {//2.同步代码块(同步方法)if (method()) break;}}private synchronized boolean method() {//3.判断共享数据是否到了末尾,如果到了末尾if (ticket == 100) {return true;} else {//4.判断共享数据是否到了末尾,如果没有到末尾try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}ticket++;System.out.println(Thread.currentThread().getName() + "在卖第" + ticket + "张票!!!");}return false;} }
package a08_同步方法;public class Test {public static void main(String[] args) { // 需求: // 某电影院目前正在上映国产大片,共有 100 张票,而它有 3 个窗口卖票,请设计一个程序模拟该电影院卖票 // 利用同步方法完成 // 技巧:同步代码块提取成方法MyRunnable mr = new MyRunnable();Thread t1 = new Thread(mr);Thread t2 = new Thread(mr);Thread t3 = new Thread(mr);t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();} }
9.lock锁
Lock 锁 虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5 以后提供了一个新的锁对象 Lock。 Lock 实现提供比使用 synchronized 方法和语句可以获得更广泛的锁定操作。Lock 中提供了获得锁和释放锁的方法: void lock ():获得锁 void unlock ():释放锁 (手动上锁、手动释放锁 ) Lock 是接口不能直接实例化,这里采用它的实现类 ReentrantLock 来实例化。 java.util.concurrent.locks.ReentrantLock 的构造方法:ReentrantLock ():创建一个 ReentrantLock 的实例
package a09_lock锁;import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;public class MyThread extends Thread {static int ticket = 0;static Lock lock = new ReentrantLock();@Overridepublic void run() {//1.循环while (true) {//2.同步代码块 // synchronized (MyThread.class) {// Lock 锁lock.lock();try {//3.判断if (ticket == 100) {break;} else {Thread.sleep(10);ticket++;System.out.println(getName() + "正在卖第" + ticket + "张票!!!");}} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}// }}} }
package a09_lock锁;public class Test {public static void main(String[] args) { // 需求:某电影院目前正在上映国产大片,共有 100 张票,而它有 3 个窗口卖票,请设计一个程序模拟该电影院卖票 // 用JDK5的lock实现MyThread t1 = new MyThread();MyThread t2 = new MyThread();MyThread t3 = new MyThread();t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();} }
10.死锁
package a10_死锁;public class MyThread extends Thread{static Object objA = new Object();static Object objB = new Object();// 死锁是一个错误@Overridepublic void run() {//1.循环while(true){if("线程A".equals(getName())){synchronized (objA){System.out.println("线程A拿到了A锁,准备拿B锁");synchronized (objB){System.out.println("线程A拿到了B锁,顺利执行完一轮");}}}else if("线程B".equals(getName())){if("线程B".equals(getName())){synchronized (objB){System.out.println("线程B拿到了B锁,准备拿A锁");synchronized (objA){System.out.println("线程B拿到了A锁,顺利执行完一轮");}}}}}} }
package a10_死锁;public class Test {public static void main(String[] args) { // 需求:死锁MyThread t1 = new MyThread();MyThread t2 = new MyThread();t1.setName("线程A");t2.setName("线程B");t1.start();t2.start();} }
11.等待唤醒机制
思路分析
生产者和消费者(等待唤醒机制)消费者操作: 1.判断桌子上是否有食物 2.如果没有就等待 3.如果有就开吃 4.吃完之后,唤醒厨师继续做生产者操作: 1.判断桌子上是否有食物 2.有:等待 3.没有:制作食物 4.把食物放在桌子上 5.叫醒等待的消费者开吃
代码实现
package a11_等待唤醒机制.a02_代码实现;public class Desk {//作用:控制生产者和消费者的执行//是否有面条 0:没有面条 1:有面条public static int foodFlag = 0;//总个数public static int count = 10;//锁对象public static Object lock = new Object(); }
package a11_等待唤醒机制.a02_代码实现;public class Cook extends Thread {/** 1. 循环* 2. 同步代码块* 3. 判断共享数据是否到了末尾(到了末尾)* 4. 判断共享数据是否到了末尾(没有到末尾,执行核心逻辑)*/@Overridepublic void run() {while (true) {synchronized (Desk.lock) {if (Desk.count == 0) {break;} else {//判断桌子上是否有食物if (Desk.foodFlag == 1) {//如果有,就等待try {Desk.lock.wait();} catch (InterruptedException e) {e.printStackTrace();}} else {//如果没有,就制作食物System.out.println("厨师做了一碗面条");//修改桌子上的食物状态Desk.foodFlag = 1;//叫醒等待的消费者开吃Desk.lock.notifyAll();}}}}} }
package a11_等待唤醒机制.a02_代码实现;public class Foodie extends Thread {@Overridepublic void run() {/** 1. 循环* 2. 同步代码块* 3. 判断共享数据是否到了末尾(到了末尾)* 4. 判断共享数据是否到了末尾(没有到末尾,执行核心逻辑)*/while (true) {synchronized (Desk.lock) {if (Desk.count == 0) {break;} else {//先判断桌子上是否有面条if (Desk.foodFlag == 0) {//如果没有,就等待try {Desk.lock.wait();//让当前线程跟锁进行绑定} catch (InterruptedException e) {e.printStackTrace();}} else {//把吃的总数-1Desk.count--;//如果有,就开吃System.out.println("吃货在吃面条,还能再吃" + Desk.count + "碗!!!");//吃完之后,唤醒厨师继续做Desk.lock.notifyAll();//修改桌子的状态Desk.foodFlag = 0;}}}}} }
package a11_等待唤醒机制.a02_代码实现;public class Test {public static void main(String[] args) {/*需求:完成生产者和消费者(等待唤醒机制)的代码实现线程轮流交替执行的效果*///创建线程的对象Cook c = new Cook();Foodie f = new Foodie();//给线程设置名字c.setName("厨师");f.setName("吃货");//开启线程c.start();f.start();} }
阻塞队列实现等待唤醒机制
package a11_等待唤醒机制.a03_阻塞队列实现等待唤醒机制;import java.util.concurrent.ArrayBlockingQueue;public class Cook extends Thread {ArrayBlockingQueue<String> queue;public Cook(ArrayBlockingQueue<String> queue) {this.queue = queue;}@Overridepublic void run() {while (true) {//不断的把面条放到阻塞队列当中try {queue.put("面条");System.out.println("厨师放了一碗面条");} catch (InterruptedException e) {e.printStackTrace();}}} }
package a11_等待唤醒机制.a03_阻塞队列实现等待唤醒机制;import java.util.concurrent.ArrayBlockingQueue;public class Foodie extends Thread{ArrayBlockingQueue<String> queue;public Foodie(ArrayBlockingQueue<String> queue) {this.queue = queue;}@Overridepublic void run() {while(true){//不断从阻塞队列中获取面条try {String food = queue.take();System.out.println(food);} catch (InterruptedException e) {e.printStackTrace();}}} }
package a11_等待唤醒机制.a03_阻塞队列实现等待唤醒机制;import java.util.concurrent.ArrayBlockingQueue;public class Test {public static void main(String[] args) { // 阻塞队列的继承结构 // 接口: // Iterable // Collection // Queue // BlockingQueue// 实现类: // ArrayBlockingQueue:底层是数组,有界 // LinkedBlockingQueue:底层是链表,无界,但不是真正的无界,最大为 int 的最大值/*需求:利用阻塞队列完成生产者和消费者(等待唤醒机制)的代码细节:生产者和消费者必须使用同一个阻塞队列*///1.创建阻塞队列的对象ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);//2.创建线程的对象,并把阻塞队列传递过去Cook c = new Cook(queue);Foodie f = new Foodie(queue);//3.开启线程c.start();f.start();} }
12.多线程的6种状态
线程的状态 新建状态(NEW):创建线程对象 就绪状态(RUNNABLE):start方法 阻塞状态(BLOCKED):无法获得锁对象 等待状态(WAITING):wait方法 计时等待(TIMED_WAITING):sleep方法 结束状态(TERMINATED):全部代码运行完毕
13.综合练习
抢红包
package a13_综合练习.a01_抢红包;import java.util.Random;public class MyThread extends Thread {//共享数据//100块,分成了3个包static double money = 100;static int count = 3;//最小的中奖金额static final double MIN = 0.01;@Overridepublic void run() {//同步代码块synchronized (MyThread.class) {if (count == 0) {//判断,共享数据是否到了末尾(已经到末尾)System.out.println(getName() + "没有抢到红包!");} else {//判断,共享数据是否到了末尾(没有到末尾)//定义一个变量,表示中奖的金额double prize = 0;if (count == 1) {//表示此时是最后一个红包//就无需随机,剩余所有的钱都是中奖金额prize = money;} else {//表示第一次,第二次(随机)Random r = new Random();//100元 3个包//第一个红包:99.98//100 - (3-1) * 0.01double bounds = money - (count - 1) * MIN;prize = r.nextDouble(bounds);if (prize < MIN) {prize = MIN;}}//从money当中,去掉当前中奖的金额money = money - prize;//红包的个数-1count--;//本次红包的信息进行打印System.out.println(getName() + "抢到了" + prize + "元");}}} }
package a13_综合练习.a01_抢红包;public class Test {public static void main(String[] args) {/*微信中的抢红包也用到了多线程。假设:100块,分成了3个包,现在有5个人去抢。其中,红包是共享数据。5个人是5条线程。打印结果如下:XXX抢到了XXX元XXX抢到了XXX元XXX抢到了XXX元XXX没抢到XXX没抢到*///创建线程的对象MyThread t1 = new MyThread();MyThread t2 = new MyThread();MyThread t3 = new MyThread();MyThread t4 = new MyThread();MyThread t5 = new MyThread();//给线程设置名字t1.setName("小A");t2.setName("小QQ");t3.setName("小哈哈");t4.setName("小涛涛");t5.setName("小丹丹");//启动线程t1.start();t2.start();t3.start();t4.start();t5.start();} }
抽奖
package a13_综合练习.a02_抽奖;import java.util.ArrayList; import java.util.Collections;public class MyThread extends Thread{ArrayList<Integer> list;public MyThread(ArrayList<Integer> list) {this.list = list;}@Overridepublic void run() {//1.循环//2.同步代码块//3.判断//4.判断while (true) {synchronized (MyThread.class) {if (list.size() == 0) {break;} else {//继续抽奖Collections.shuffle(list);int prize = list.remove(0);System.out.println(getName() + "又产生了一个" + prize + "元大奖");}}try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}} }
package a13_综合练习.a02_抽奖;import java.util.ArrayList; import java.util.Collections;public class Test {public static void main(String[] args) {/*有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为 {10,5,20,50,100,200,500,800,2,80,300,700};创建两个抽奖箱(线程)设置线程名称分别为"抽奖箱1","抽奖箱2"随机从抽奖池中获取奖项元素并打印在控制台,格式如下:每次抽出一个奖项就打印一个(随机)抽奖箱1 又产生了一个 10 元大奖抽奖箱1 又产生了一个 100 元大奖抽奖箱1 又产生了一个 200 元大奖抽奖箱1 又产生了一个 800 元大奖抽奖箱2 又产生了一个 700 元大奖......*///创建奖池ArrayList<Integer> list = new ArrayList<>();Collections.addAll(list, 10,5,20,50,100,200,500,800,2,80,300,700);//创建线程MyThread t1 = new MyThread(list);MyThread t2 = new MyThread(list);//设置名字t1.setName("抽奖箱1");t2.setName("抽奖箱2");//启动线程t1.start();t2.start();} }
多线程统计并求最大值解法一
package a13_综合练习.a03_多线程统计并求最大值.a01_解法一;import java.util.ArrayList; import java.util.Collections;public class MyThread extends Thread{ArrayList<Integer> list;public MyThread(ArrayList<Integer> list) {this.list = list;}//线程一static ArrayList<Integer> list1 = new ArrayList<>();//线程二static ArrayList<Integer> list2 = new ArrayList<>();@Overridepublic void run() {while (true) {synchronized (MyThread.class) {if (list.size() == 0) {if("抽奖箱1".equals(getName())){System.out.println("抽奖箱1" + list1);}else {System.out.println("抽奖箱2" + list2);}break;} else {//继续抽奖Collections.shuffle(list);int prize = list.remove(0);if("抽奖箱1".equals(getName())){list1.add(prize);}else {list2.add(prize);}}}try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}} }
package a13_综合练习.a03_多线程统计并求最大值.a01_解法一;import java.util.ArrayList; import java.util.Collections;public class Test {public static void main(String[] args) { // 有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为 {10,5,20,50,100,200,500,800,2,80,300,700} // 创建两个抽奖箱(线程)设置线程名称分别为"抽奖箱1","抽奖箱2" // 随机从抽奖池中获取奖项元素并打印在控制台上,格式如下: // 每次抽的过程中,不打印,抽完时一次性打印(随机) // 在此次抽奖过程中,抽奖箱1总共产了6个奖项。 // 分别为: 10,20,100,500,2,300最高奖项为300元,总计额为932元 // 在此次抽奖过程中,抽奖箱2总共产了6个奖项。 // 分别为: 5,50,200,800,80,700最高奖项为800元,总计额为1835元//创建奖池ArrayList<Integer> list = new ArrayList<>();Collections.addAll(list, 10,5,20,50,100,200,500,800,2,80,300,700);//创建线程MyThread t1 = new MyThread(list);MyThread t2 = new MyThread(list);//设置名字t1.setName("抽奖箱1");t2.setName("抽奖箱2");//启动线程t1.start();t2.start();} }
多线程统计并求最大值解法二(线程栈)
package a13_综合练习.a03_多线程统计并求最大值.a02_解法二_线程栈;import java.util.ArrayList; import java.util.Collections;public class MyThread extends Thread {ArrayList<Integer> list;public MyThread(ArrayList<Integer> list) {this.list = list;}@Overridepublic void run() {ArrayList<Integer> boxList = new ArrayList<>();while (true) {synchronized (MyThread.class) {if (list.size() == 0) {System.out.println(getName() + boxList);break;} else {//继续抽奖Collections.shuffle(list);int prize = list.remove(0);boxList.add(prize);}}try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}} }
package a13_综合练习.a03_多线程统计并求最大值.a02_解法二_线程栈;import java.util.ArrayList; import java.util.Collections;public class Test {public static void main(String[] args) { // 有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为 {10,5,20,50,100,200,500,800,2,80,300,700} // 创建两个抽奖箱(线程)设置线程名称分别为"抽奖箱1","抽奖箱2" // 随机从抽奖池中获取奖项元素并打印在控制台上,格式如下: // 每次抽的过程中,不打印,抽完时一次性打印(随机) // 在此次抽奖过程中,抽奖箱1总共产了6个奖项。 // 分别为: 10,20,100,500,2,300最高奖项为300元,总计额为932元 // 在此次抽奖过程中,抽奖箱2总共产了6个奖项。 // 分别为: 5,50,200,800,80,700最高奖项为800元,总计额为1835元//创建奖池ArrayList<Integer> list = new ArrayList<>();Collections.addAll(list, 10,5,20,50,100,200,500,800,2,80,300,700);//创建线程MyThread t1 = new MyThread(list);MyThread t2 = new MyThread(list);//设置名字t1.setName("抽奖箱1");t2.setName("抽奖箱2");//启动线程t1.start();t2.start();} }
多线程之间的比较
package a13_综合练习.a04_多线程之间的比较;import java.util.ArrayList; import java.util.Collections; import java.util.concurrent.Callable;public class MyCallable implements Callable<Integer> {ArrayList<Integer> list;public MyCallable(ArrayList<Integer> list) {this.list = list;}@Overridepublic Integer call() throws Exception {ArrayList<Integer> boxList = new ArrayList<>();while (true) {synchronized (MyCallable.class) {if (list.size() == 0) {System.out.println(Thread.currentThread().getName() + boxList);break;} else {//继续抽奖Collections.shuffle(list);int prize = list.remove(0);boxList.add(prize);}}Thread.sleep(10);}//把集合中的最大值返回if(boxList.size() == 0){return null;}else{return Collections.max(boxList);}} }
package a13_综合练习.a04_多线程之间的比较;import java.util.ArrayList; import java.util.Collections; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask;public class Test {public static void main(String[] args) throws ExecutionException, InterruptedException {/*有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为 {10,5,20,50,100,200,500,800,2,80,300,700};创建两个抽奖箱(线程)设置线程名称分别为 "抽奖箱1", "抽奖箱2"随机从抽奖池中获取奖项元素并打印在控制台上,格式如下:在此次抽奖过程中,抽奖箱1总共产了6个奖项,分别为: 10,20,100,500,2,300最高奖项为300元,总计额为932元在此次抽奖过程中,抽奖箱2总共产了6个奖项,分别为: 5,50,200,800,80,700最高奖项为800元,总计额为1835元在此次抽奖过程中,抽奖箱2中产生了最大奖项,该奖项金额为800元以上打印效果只是数据模拟,实际代码运行的效果会有差异*///创建奖池ArrayList<Integer> list = new ArrayList<>();Collections.addAll(list, 10, 5, 20, 50, 100, 200, 500, 800, 2, 80, 300, 700);//创建多线程要运行的参数对象MyCallable mc = new MyCallable(list);//创建多线程运行结果的管理者对象//线程一FutureTask<Integer> ft1 = new FutureTask<>(mc);//线程二FutureTask<Integer> ft2 = new FutureTask<>(mc);//创建线程对象Thread t1 = new Thread(ft1);Thread t2 = new Thread(ft2);//设置名字t1.setName("抽奖箱1");t2.setName("抽奖箱2");//开启线程t1.start();t2.start();Integer max1 = ft1.get();Integer max2 = ft2.get();System.out.println(max1);System.out.println(max2);} }
14.线程池
以前写多线程的弊端 弊端1:用到线程的时候就创建 弊端2:用完之后线程消失线程池主要核心原理 1.创建一个池子,池子中是空的 2.提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子下回再次提交任务时,不需要创建新的线程,直接复用已有的线程即可 3.但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待线程池代码实现 Executors:线程池的工具类通过调用方法返回不同类型的线程池对象。方法名称 说明 public static ExecutorService newCachedThreadPool() 创建一个没有上限的线程池 public static ExecutorService newFixedThreadPool(int nThreads) 创建有上限的线程池
package a14_线程池;public class MyRunnable implements Runnable {@Overridepublic void run() {for (int i = 1; i <= 100; i++) {System.out.println(Thread.currentThread().getName() + "---" + i);}} }
//1.获取线程池对象 ExecutorService pool1 = Executors.newCachedThreadPool();//2.提交任务 pool1.submit(new MyRunnable()); pool1.submit(new MyRunnable()); pool1.submit(new MyRunnable()); pool1.submit(new MyRunnable()); pool1.submit(new MyRunnable());//3.销毁线程池 pool1.shutdown();
15.自定义线程池
自定义线程池小结创建一个空的池子有任务提交时,线程池会创建线程去执行任务,执行完毕归还线程不断的提交任务,会有以下三个临界点:当核心线程满时,再提交任务就会排队当核心线程满,队伍满时,会创建临时线程当核心线程满,队伍满,临时线程满时,会触发任务拒绝策略自定义线程池 (任务拒绝策略) 任务拒绝策略 说明 ThreadPoolExecutor.AbortPolicy 默认策略:丢弃任务并抛出 RejectedExecutionException 异常 ThreadPoolExecutor.DiscardPolicy 丢弃任务,但是不抛出异常 这是不推荐的做法 ThreadPoolExecutor.DiscardOldestPolicy 抛弃队列中等待最久的任务 然后把当前任务加入队列中 ThreadPoolExecutor.CallerRunsPolicy 调用任务的 run () 方法绕过线程池直接执行
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor (核心线程数量,最大线程数量,空闲线程最大存活时间,任务队列,创建线程工厂,任务的拒绝策略);参数一:核心线程数量 不能小于0 参数二:最大线程数 不能小于等于0,最大数量 >= 核心线程数量 参数三:空闲线程最大存活时间 不能小于0 参数四:时间单位 用TimeUnit指定 参数五:任务队列 不能为null 参数六:创建线程工厂 不能为null 参数七:任务的拒绝策略 不能为null
ThreadPoolExecutor pool = new ThreadPoolExecutor(3, //核心线程数量,能小于06, //最大线程数,不能小于0,最大数量 >= 核心线程数量60, //空闲线程最大存活时间TimeUnit.SECONDS, //时间单位new ArrayBlockingQueue<>(3), //任务队列Executors.defaultThreadFactory(), //创建线程工厂new ThreadPoolExecutor.AbortPolicy() //任务的拒绝策略 );
16.最大并行数
package a16_最大并行数;public class Test {public static void main(String[] args) {//向Java虚拟机返回可用处理器的数目int count = Runtime.getRuntime().availableProcessors();System.out.println(count);} }
17.线程池多大合适
线程池多大合适呢? CPU密集型运算:最大并行数 + 1 I/O密集型运算:最大并行数 * 期望CPU利用率 * (总时间(CPU计算时间 + 等待时间)/CPU计算时间) 举例:从本地文件中,读取两个数据,并进行相加 操作一:读取两个数据(1 秒钟) 操作二:相加(1 秒钟) 4核8线程:8 * 100% * ( 100% / 50%)
网络编程
1.初识网络编程
什么是网络编程? 在网络通信协议下,不同计算机上运行的程序,进行的数据传输。 应用场景:即时通信、网游对战、金融证券、国际贸易、邮件、等等。不管是什么场景,都是计算机跟计算机之间通过网络进行数据传输。 Java 中可以使用java.net包下的技术轻松开发出常见的网络应用程序。 常见的软件架构:BS、CSBS/CS 架构的优缺点 应用软件的架构分类 BS01:不需要开发客户端,只需要开发服务端02:用户不需要下载,打开浏览器就能使用03:如果应用过大,用户体验受到影响 CS01:画面可以做的非常精美,用户体验好02:需要开发客户端,也需要开发服务端03:用户需要下载和更新的时候太麻烦
2.网络编程三要素
网络编程三要素 IP设备在网络中的地址,是唯一的标识 端口号应用程序在设备中唯一的标识 协议数据在网络中传输的规则,常见的协议有 UDP、TCP、http、https、ftp
IP
1.Ip 的作用设备在网络中的地址,是唯一的标识 2.IPv4 有什么特点目前的主流方案最多只有 2^32 次方个 ip,目前已经用完了 3.IPv6 有什么特点为了解决 IPv4 不够用而出现的最多有 2^128 次方个 ip可以为地球上的每一粒沙子都设定 ip
ipv4的一些小细节
现在如何解决IPv4不够的问题?利用局域网IP解决IP不够的问题 特殊的IP是什么?127.0.0.1(永远表示本机) 常见的两个 CMD 命令?ipconfig:查看本机IP地址ping:检查网络是否连通
InetAddress类的使用
static InetAddress getByName(String host) 确定主机名称的IP地址。主机名称可以是机器名称,也可以是IP地址 String getHostName() 获取此IP地址的主机名 String getHostAddress() 返回文本显示中的IP地址字符串
package a02_网络编程三要素.a02_IP;import java.net.InetAddress; import java.net.UnknownHostException;public class a03_InetAddress类的使用 {public static void main(String[] args) throws UnknownHostException {//1.获取InetAddress的对象//IP的对象 一台电脑的对象InetAddress address = InetAddress.getByName("魏巍");System.out.println(address);String name = address.getHostName();System.out.println(name);String ip = address.getHostAddress();System.out.println(ip);//192.168.58.165} }
3.端口号
端口号 应用程序在设备中唯一的标识端口号:由两个字节表示的整数,取值范围:0~65535 其中 0~1023 之间的端口号用于一些知名的网络服务或者应用 我们自己使用 1024 以上的端口号就可以了注意:一个端口号只能被一个应用程序使用
4.协议
UDP协议和TCP协议简介
协议:计算机网络中,连接和通信的规则被称为网络通信协议 UDP 协议 用户数据报协议 (User Datagram Protocol) UDP 是面向无连接通信协议 速度快,有大小限制一次最多发送 64K,数据不安全,易丢失数据 TCP 协议 传输控制协议 TCP (Transmission Control Protocol) TCP 协议是面向连接的通信协议 速度慢,没有大小限制,数据安全
UDP协议
UDP 通信程序(发送数据) 创建发送端的 DatagramSocket 对象 数据打包(DatagramPacket) 发送数据 释放资源
package a02_网络编程三要素.a04_协议.a02_UDP协议;import java.io.IOException; import java.net.*;public class a01_发送数据 {public static void main(String[] args) throws IOException {//发送数据//1.创建DatagramSocket对象(快递公司)//细节://绑定端口,以后我们就是通过这个端口往外发送//空参:所有可用的端口中随机一个进行使用//有参:指定端口号进行绑定DatagramSocket ds = new DatagramSocket();//2.打包数据String str = "你好啊!!!";byte[] bytes = str.getBytes();InetAddress address = InetAddress.getByName("127.0.0.1");int port = 10086;DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);//3.发送数据ds.send(dp);//4.释放资源ds.close();} }
UDP 通信程序(接收数据) 创建接收端的 DatagramSocket 对象 接收打包好的数据 解析数据包 释放资源
package a02_网络编程三要素.a04_协议.a02_UDP协议;import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress;public class a02_接收数据 {public static void main(String[] args) throws IOException {//接收数据//1.创建DatagramSocket对象(快递公司)//细节://在接收的时候,一定要绑定端口//而且绑定的端口一定要跟发送的端口保持一致DatagramSocket ds = new DatagramSocket(10086);//2.接收数据包byte[] bytes = new byte[1024];DatagramPacket dp = new DatagramPacket(bytes,bytes.length);//该方法是阻塞的//程序执行到这一步的时候,会在这里死等//等发送端发送消息System.out.println(11111);ds.receive(dp);System.out.println(22222);//3.解析数据包byte[] data = dp.getData();int len = dp.getLength();InetAddress address = dp.getAddress();int port = dp.getPort();System.out.println("接收到数据" + new String(data, 0,len));System.out.println("该数据是从" + address + "这台电脑中的" + port + "这个端口发出的");//4.释放资源ds.close();} }
单播、组播、广播
UDP 的三种通信方式(代码实现) 单播以前的代码就是单播 组播组播地址:224.0.0.0 ~ 239.255.255.255其中 224.0.0.0 ~ 224.0.0.255 为预留的组播地址 广播广播地址:255.255.255.255
package a02_网络编程三要素.a04_协议.a04_单播_组播_广播;import java.io.IOException; import java.net.DatagramPacket; import java.net.InetAddress; import java.net.MulticastSocket;public class SendMessage {public static void main(String[] args) throws IOException {/*组播发送端代码*///创建MulticastSocket对象MulticastSocket ms = new MulticastSocket();// 创建DatagramPacket对象String s = "你好,你好!";byte[] bytes = s.getBytes(); // 组播InetAddress address = InetAddress.getByName("224.0.0.1"); // 广播 // InetAddress address = InetAddress.getByName("255.255.255.255");int port = 10000;DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, address, port);// 调用MulticastSocket发送数据方法发送数据ms.send(datagramPacket);// 释放资源ms.close();} }
package a02_网络编程三要素.a04_协议.a04_单播_组播_广播;import java.io.IOException; import java.net.DatagramPacket; import java.net.InetAddress; import java.net.MulticastSocket;public class ReceiveMessage1 {public static void main(String[] args) throws IOException {/*组播接收端代码*///1.创建MulticastSocket对象MulticastSocket ms = new MulticastSocket(10000);//2.将当前本机,添加到224.0.0.1的这一组当中InetAddress address = InetAddress.getByName("224.0.0.1");ms.joinGroup(address);//3.创建DatagramPacket数据包对象byte[] bytes = new byte[1024];DatagramPacket dp = new DatagramPacket(bytes, bytes.length);//4.接收数据ms.receive(dp);//5.解析数据byte[] data = dp.getData();int len = dp.getLength();String ip = dp.getAddress().getHostAddress();String name = dp.getAddress().getHostName();System.out.println("ip为:" + ip + ",主机名为:" + name + "的人,发送了数据:" + new String(data, 0,len));//6.释放资源ms.close();} }
package a02_网络编程三要素.a04_协议.a04_单播_组播_广播;import java.io.IOException; import java.net.DatagramPacket; import java.net.InetAddress; import java.net.MulticastSocket;public class ReceiveMessage2 {public static void main(String[] args) throws IOException {/*组播接收端代码*///1.创建MulticastSocket对象MulticastSocket ms = new MulticastSocket(10000);//2.将当前本机,添加到224.0.0.1的这一组当中InetAddress address = InetAddress.getByName("224.0.0.1");ms.joinGroup(address);//3.创建DatagramPacket数据包对象byte[] bytes = new byte[1024];DatagramPacket dp = new DatagramPacket(bytes, bytes.length);//4.接收数据ms.receive(dp);//5.解析数据byte[] data = dp.getData();int len = dp.getLength();String ip = dp.getAddress().getHostAddress();String name = dp.getAddress().getHostName();System.out.println("ip为:" + ip + ",主机名为:" + name + "的人,发送了数据:" + new String(data, 0,len));//6.释放资源ms.close();} }
package a02_网络编程三要素.a04_协议.a04_单播_组播_广播;import java.io.IOException; import java.net.DatagramPacket; import java.net.InetAddress; import java.net.MulticastSocket;public class ReceiveMessage3 {public static void main(String[] args) throws IOException {/*组播接收端代码*///1.创建MulticastSocket对象MulticastSocket ms = new MulticastSocket(10000);//2.将当前本机,添加到224.0.0.1的这一组当中InetAddress address = InetAddress.getByName("224.0.0.1");ms.joinGroup(address);//3.创建DatagramPacket数据包对象byte[] bytes = new byte[1024];DatagramPacket dp = new DatagramPacket(bytes, bytes.length);//4.接收数据ms.receive(dp);//5.解析数据byte[] data = dp.getData();int len = dp.getLength();String ip = dp.getAddress().getHostAddress();String name = dp.getAddress().getHostName();System.out.println("ip为:" + ip + ",主机名为:" + name + "的人,发送了数据:" + new String(data, 0,len));//6.释放资源ms.close();} }
TCP协议
TCP 通信程序TCP 通信协议是一种可靠的网络协议,它在通信的两端各建立一个 Socket 对象通信之前要保证连接已经建立通过 Socket 产生 IO 流来进行网络通信客户端 1.创建客户端的 Socket 对象 (Socket) 与指定服务端连接 Socket (String host, int port) 2.获取输出流,写数据 OutputStream getOutputStream () 3.释放资源 void close ()服务器端 1.创建服务器端的 Socket 对象 (ServerSocket) ServerSocket (int port) 2.监听客户端连接,返回一个 Socket 对象 java.net.Socket accept () 3.获取输入流,读数据,并把数据显示在控制台 InputStream getInputStream () 4.释放资源 void close ()
package a02_网络编程三要素.a04_协议.a05_TCP协议;import java.io.IOException; import java.io.OutputStream; import java.net.Socket;public class Client {public static void main(String[] args) throws IOException {//TCP协议,发送数据//1.创建Socket对象//细节:在创建对象的同时会连接服务端// 如果连接不上,代码会报错Socket socket = new Socket("127.0.0.1", 10086);//2.可以从连接通道中获取输出流OutputStream os = socket.getOutputStream();//写出数据os.write("你好你好".getBytes());//3.释放资源os.close();socket.close();} }
package a02_网络编程三要素.a04_协议.a05_TCP协议;import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socket;public class Server {public static void main(String[] args) throws IOException {//TCP协议,接收数据//1.创建对象ServerSocketServerSocket ss = new ServerSocket(10086);//2.监听客户端的链接Socket socket = ss.accept();//3.从连接通道中获取输入流读取数据 // InputStream is = socket.getInputStream(); // InputStreamReader isr = new InputStreamReader(is); // BufferedReader br = new BufferedReader(isr);BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));int b;while ((b = br.read()) != -1) {System.out.print((char) b);}//4.释放资源socket.close();ss.close();} }
3.三次握手和四次挥手
TCP 通信程序(三次握手) 客户端向服务器发出连接请求,等待服务器确认 服务器向客户端返回一个响应,告诉客户端收到了请求 客户端向服务器再次发出确认信息,连接建立TCP 通信程序(四次挥手) 客户端向服务器发出取消连接请求 服务器向客户端返回一个响应,表示收到客户端取消请求 服务器向客户端发出确认取消信息(服务器将最后的数据处理完毕 ) 客户端再次发送确认消息,连接取消
4.综合练习
多发多收
package a02_网络编程三要素.a04_协议.a07_综合练习.a01_多发多收;import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import java.util.Scanner;public class Client {public static void main(String[] args) throws IOException {//客户端:多次发送数据//服务器:接收多次接收数据,并打印//1. 创建Socket对象并连接服务端Socket socket = new Socket("127.0.0.1", 10000);//2.写出数据Scanner sc = new Scanner(System.in);OutputStream os = socket.getOutputStream();while (true) {System.out.println("请输入您要发送的信息");String str = sc.nextLine();if("886".equals(str)){break;}os.write(str.getBytes());}//3.释放资源socket.close();} }
package a02_网络编程三要素.a04_协议.a07_综合练习.a01_多发多收;import java.io.IOException; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socket;public class Server {public static void main(String[] args) throws IOException {//客户端:多次发送数据//服务器:接收多次接收数据,并打印//1.创建对象绑定10000端口ServerSocket ss = new ServerSocket(10000);//2.等待客户端来连接Socket socket = ss.accept();//3.读取数据InputStreamReader isr = new InputStreamReader(socket.getInputStream());int b;while ((b = isr.read()) != -1){System.out.print((char)b);}//4.释放资源socket.close();ss.close();} }
接收并反馈
package a04_综合练习.a03_上传文件;import java.io.*; import java.net.Socket;public class Client {public static void main(String[] args) throws IOException {//客户端:将本地文件上传到服务器。接收服务器的反馈。//服务器:接收客户端上传的文件,上传完毕之后给出反馈。//1. 创建Socket对象,并连接服务器Socket socket = new Socket("127.0.0.1", 10086);//2.读取本地文件中的数据,并写到服务器当中BufferedInputStream bis = new BufferedInputStream(new FileInputStream("mysocketnet\\clientdir\\a.jpg"));BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());byte[] bytes = new byte[1024];int len;while ((len = bis.read(bytes)) != -1){bos.write(bytes, 0,len);}//往服务器写出结束标记socket.shutdownOutput();//3.接收服务器的回写数据BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));String line = br.readLine();System.out.println(line);//4.释放资源socket.close();} }
package a04_综合练习.a02_接收并反馈;import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Socket;public class Client {public static void main(String[] args) throws IOException {//客户端:发送一条数据,接收服务端反馈的消息并打印//服务器:接收数据并打印,再给客户端反馈消息//1.创建Socket对象并连接服务端Socket socket = new Socket("127.0.0.1", 10086);//2.写出数据String str = "见到你很高兴!";OutputStream os = socket.getOutputStream();os.write(str.getBytes());//写出一个结束标记socket.shutdownOutput();//3.接收服务端回写的数据InputStream is = socket.getInputStream();InputStreamReader isr = new InputStreamReader(is);int b;while ((b = isr.read()) != -1){System.out.print((char)b);}//释放资源socket.close();} }
上传文件
package a04_综合练习.a03_上传文件;import java.io.*; import java.net.Socket;public class Client {public static void main(String[] args) throws IOException {//客户端:将本地文件上传到服务器。接收服务器的反馈。//服务器:接收客户端上传的文件,上传完毕之后给出反馈。//1. 创建Socket对象,并连接服务器Socket socket = new Socket("127.0.0.1", 10086);//2.读取本地文件中的数据,并写到服务器当中BufferedInputStream bis = new BufferedInputStream(new FileInputStream("mysocketnet\\clientdir\\a.jpg"));BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());byte[] bytes = new byte[1024];int len;while ((len = bis.read(bytes)) != -1){bos.write(bytes, 0,len);}//往服务器写出结束标记socket.shutdownOutput();//3.接收服务器的回写数据BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));String line = br.readLine();System.out.println(line);//4.释放资源socket.close();} }
package a04_综合练习.a03_上传文件;import java.io.*; import java.net.ServerSocket; import java.net.Socket;public class Server {public static void main(String[] args) throws IOException {//客户端:将本地文件上传到服务器。接收服务器的反馈。//服务器:接收客户端上传的文件,上传完毕之后给出反馈。//1.创建对象并绑定端口ServerSocket ss = new ServerSocket(10000);//2.等待客户端来连接Socket socket = ss.accept();//3.读取数据并保存到本地文件中BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("mysocketnet\\serverdir\\a.jpg"));int len;byte[] bytes = new byte[1024];while ((len = bis.read(bytes)) != -1){bos.write(bytes, 0,len);}//4.回写数据BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));bw.write("上传成功");bw.newLine();bw.flush();//5.释放资源socket.close();ss.close();} }
文件名重复
String name = UUID.randomUUID().toString().replace("-", ""); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("mysocketnet\\serverdir\\name.jpg"));
多线程版的服务端
package a04_TCP综合练习.a05_多线程版的服务端;import java.io.IOException; import java.net.ServerSocket; import java.net.Socket;public class Server {public static void main(String[] args) throws IOException {//客户端:将本地文件上传到服务器。接收服务器的反馈。//服务器:接收客户端上传的文件,上传完毕之后给出反馈。//1.创建对象并绑定端口ServerSocket ss = new ServerSocket(10000);while (true) {//2.等待客户端来连接Socket socket = ss.accept();//开启一条线程//一个用户就对应服务端的一条线程new Thread(new MyRunnable(socket)).start();}} }
package a04_TCP综合练习.a05_多线程版的服务端;import java.io.*; import java.net.Socket; import java.util.UUID;public class MyRunnable implements Runnable {Socket socket;public MyRunnable(Socket socket){this.socket = socket;}@Overridepublic void run() {try {//3.读取数据并保存到本地文件中BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());String name = UUID.randomUUID().toString().replace("-", "");BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("mysocketnet\\serverdir\\name.jpg"));int len;byte[] bytes = new byte[1024];while ((len = bis.read(bytes)) != -1) {bos.write(bytes, 0, len);}//4.回写数据BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));bw.write("上传成功");bw.newLine();bw.flush();} catch (IOException e) {e.printStackTrace();} finally {//5.释放资源if(socket != null){try {socket.close();} catch (IOException e) {e.printStackTrace();}}}} }
package a04_TCP综合练习.a05_多线程版的服务端;import java.io.*; import java.net.Socket;public class Client {public static void main(String[] args) throws IOException {//客户端:将本地文件上传到服务器。接收服务器的反馈。//服务器:接收客户端上传的文件,上传完毕之后给出反馈。//1. 创建Socket对象,并连接服务器Socket socket = new Socket("127.0.0.1", 10086);//2.读取本地文件中的数据,并写到服务器当中BufferedInputStream bis = new BufferedInputStream(new FileInputStream("mysocketnet\\clientdir\\a.jpg"));BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());byte[] bytes = new byte[1024];int len;while ((len = bis.read(bytes)) != -1){bos.write(bytes, 0,len);}//往服务器写出结束标记socket.shutdownOutput();//3.接收服务器的回写数据BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));String line = br.readLine();System.out.println(line);//4.释放资源socket.close();} }
线程池版的服务端
package a04_TCP综合练习.a06_线程池版的服务端;import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit;public class Server {public static void main(String[] args) throws IOException {//客户端:将本地文件上传到服务器。接收服务器的反馈。//服务器:接收客户端上传的文件,上传完毕之后给出反馈。//创建线程池对象ThreadPoolExecutor pool = new ThreadPoolExecutor(3,//核心线程数量16,//线程池总大小60,//空闲时间TimeUnit.SECONDS,//空闲时间(单位)new ArrayBlockingQueue<>(2),//队列Executors.defaultThreadFactory(),//线程工厂,让线程池如何创建线程对象new ThreadPoolExecutor.AbortPolicy()//阻塞队列);//1.创建对象并绑定端口ServerSocket ss = new ServerSocket(10000);while (true) {//2.等待客户端来连接Socket socket = ss.accept();//开启一条线程//一个用户就对应服务端的一条线程 // new Thread(new MyRunnable(socket)).start();pool.submit(new MyRunnable(socket));}} }
反射
1.概述
什么是反射?反射允许对成员变量,成员方法和构造方法的信息进行编程访问 获取 解剖 获取 class 对象 字段(成员变量) 获取修饰符 获取名字 获取类型 赋值 / 获取值 构造方法 获取修饰符 获取名字 获取形参 创建对象 成员方法 获取修饰符 获取名字 获取形参 获取返回值 抛出的异常 获取注解 运行方法
2.获取class对象的三种方式
package a02_获取class对象的三种方式;public class Test {public static void main(String[] args) throws ClassNotFoundException {/** 获取class对象的三种方式:* 1. Class.forName("全类名");* 2. 类名.class* 3. 对象.getClass();*///1. 第一种方式//全类名 : 包名 + 类名//最为常用的Class clazz1 = Class.forName("a02_获取class对象的三种方式.Student");//2. 第二种方式//一般更多的是当做参数进行传递Class clazz2 = Student.class;synchronized (Student.class){}//3.第三种方式//当我们已经有了这个类的对象时,才可以使用。Student s = new Student();Class clazz3 = s.getClass();System.out.println(clazz1 == clazz2);System.out.println(clazz2 == clazz3);} }
3.反射获取构造方法
Class类中用于获取构造方法的方法Constructor<?>[] getConstructors()Constructor<?>[] getDeclaredConstructors()Constructor<T> getConstructor(Class<?>... parameterTypes)Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)Constructor类中用于创建对象的方法T newInstance(Object... initargs)setAccessible(boolean flag)
package a03_反射获取构造方法;import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Parameter;public class Test {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {//1.获取class字节码文件对象Class clazz = Class.forName("a03_反射获取构造方法.Student");//2.获取构造方法/*Constructor[] cons1 = clazz.getConstructors();for (Constructor con : cons1) {System.out.println(con);}Constructor[] cons2 = clazz.getDeclaredConstructors();for (Constructor con : cons2) {System.out.println(con);}*/Constructor con1 = clazz.getDeclaredConstructor();System.out.println(con1);Constructor con2 = clazz.getDeclaredConstructor(String.class);System.out.println(con2);Constructor con3 = clazz.getDeclaredConstructor(int.class);System.out.println(con3);Constructor con4 = clazz.getDeclaredConstructor(String.class, int.class);System.out.println(con4);int modifiers = con4.getModifiers(); // 获取权限修饰符 返回值是数字System.out.println(modifiers);Parameter[] parameters = con4.getParameters(); // 获取参数for (Parameter parameter : parameters) {System.out.println(parameter);}con4.setAccessible(true); //暴力反射:表示临时取消权限校验(原本con4是private修饰 创建不了对象)Student stu = (Student) con4.newInstance("张三", 23); //创建对象System.out.println(stu);} }
package a03_反射获取构造方法;public class Student {private String name;private int age;public Student() {}public Student(String name) {this.name = name;}protected Student(int age) {this.age = age;}private Student(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;} }
4.反射获取成员变量
Class类中用于获取成员变量的方法Field[] getFields(): 返回所有公共成员变量对象的数组Field[] getDeclaredFields(): 返回所有成员变量对象的数组Field getField(String name): 返回单个公共成员变量对象Field getDeclaredField(String name): 返回单个成员变量对象Field类中用于创建对象的方法void set(Object obj, Object value): 赋值Object get(Object obj) 获取值
package a04_反射获取成员变量;import java.lang.reflect.Field;public class Test {public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, NoSuchFieldException {//1.获取class字节码文件的对象Class clazz = Class.forName("a04_反射获取成员变量.Student");//2.获取成员变量Field[] fields1 = clazz.getFields(); // 公共成员变量Field[] fields2 = clazz.getDeclaredFields(); // 所有成员变量//获取单个的成员变量Field name = clazz.getDeclaredField("name");System.out.println(name);//获取权限修饰符int modifiers = name.getModifiers();System.out.println(modifiers);//获取成员变量的名字String n = name.getName();System.out.println(n);//获取成员变量的数据类型Class<?> type = name.getType();System.out.println(type);//获取成员变量记录的值Student s = new Student("zhangsan", 23, "男");name.setAccessible(true);String value = (String) name.get(s);System.out.println(value);//修改对象里面记录的值name.set(s, "lisi");System.out.println(s);} }
package a04_反射获取成员变量;public class Student {private String name;private int age;public String gender;public Student() {}public Student(String name, int age, String gender) {this.name = name;this.age = age;this.gender = gender;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;} }
5.反射获取成员方法
Class 类中用于获取成员方法的方法Method[] getMethods ():返回所有公共成员方法对象的数组,包括继承的Method[] getDeclaredMethods ():返回所有成员方法对象的数组,不包括继承的Method getMethod (String name, Class < ?>...parameterTypes) :返回单个公共成员方法对象Method getDeclaredMethod (String name, Class < ?>...parameterTypes):返回单个成员方法对象 Method 类中用于创建对象的方法Object invoke (Object obj, Object...args):运行方法参数一:用 obj 对象调用该方法参数二:调用方法的传递的参数(如果没有就不写)返回值:方法的返回值(如果没有就不写)获取方法的修饰符 获取方法的名字 获取方法的形参 获取方法的返回值 获取方法的抛出的异常
package a05_反射获取成员方法;import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Parameter;public class Test {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {//1. 获取class字节码文件对象Class clazz = Class.forName("a05_反射获取成员方法.Student");//2. 获取里面所有的方法对象(包含父类中所有的公共方法)/*Method[] methods = clazz.getMethods();for (Method method : methods) {System.out.println(method);}*/// 获取里面所有的方法对象(不能获取父类的,但是可以获取本类中私有的方法)Method[] methods = clazz.getDeclaredMethods();for (Method method : methods) {System.out.println(method);}// 获取指定的单一方法Method m = clazz.getDeclaredMethod("eat", String.class);// 获取方法的修饰符int modifiers = m.getModifiers();System.out.println(modifiers);// 获取方法的名字String name = m.getName();System.out.println(name);// 获取方法的形参Parameter[] parameters = m.getParameters();for (Parameter parameter : parameters) {System.out.println(parameter);}//获取方法的抛出的异常Class[] exceptionTypes = m.getExceptionTypes();for (Class exceptionType : exceptionTypes) {System.out.println(exceptionType);}// Method 类中用于创建对象的方法 // Object invoke (Object obj, Object...args):运行方法 // 参数一:用 obj 对象调用该方法 // 参数二:调用方法的传递的参数(如果没有就不写) // 返回值:方法的返回值(如果没有就不写)Student s = new Student();m.setAccessible(true);//参数一s:表示方法的调用者//参数二"汉堡包":表示在调用方法的时候传递的实际参String result = (String) m.invoke(s, "汉堡包");System.out.println(result);} }
package a05_反射获取成员方法;import java.io.IOException;public class Student {private String name;private int age;public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public void sleep() {System.out.println("睡觉");}private String eat(String something) throws IOException, NullPointerException, ClassCastException {System.out.println("在吃" + something);return "奥里给";}private void eat(String something, int a) {System.out.println("在吃" + something);}public String toString() {return "Student{name = " + name + ", age = " + age + "}";} }
6.综合练习
保存任意对象数据
package a06_综合练习.a01_保存任意对象数据;import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.lang.reflect.Field;public class Test {public static void main(String[] args) throws IllegalAccessException, IOException {/*对于任意一个对象,都可以把对象所有的字段名和值,保存到文件中去*/Student s = new Student("小A", 23, '女', 167.5, "睡觉");Teacher t = new Teacher("播妞", 10000);saveObject(s);}//把对象里面所有的成员变量名和值保存到本地文件中public static void saveObject(Object obj) throws IllegalAccessException, IOException {//1.获取字节码文件的对象Class clazz = obj.getClass();//2. 创建IO流BufferedWriter bw = new BufferedWriter(new FileWriter("myreflect\\a.txt"));//3. 获取所有的成员变量Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {field.setAccessible(true);//获取成员变量的名字String name = field.getName();//获取成员变量的值Object value = field.get(obj);//写出数据bw.write(name + "=" + value);bw.newLine();}bw.close();} }
package a06_综合练习.a01_保存任意对象数据;public class Student {private String name;private int age;private char gender;private double height;private String hobby;public Student() {}public Student(String name, int age, char gender, double height, String hobby) {this.name = name;this.age = age;this.gender = gender;this.height = height;this.hobby = hobby;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public char getGender() {return gender;}public void setGender(char gender) {this.gender = gender;}public double getHeight() {return height;}public void setHeight(double height) {this.height = height;}public String getHobby() {return hobby;}public void setHobby(String hobby) {this.hobby = hobby;} }
package a06_综合练习.a01_保存任意对象数据;public class Teacher {private String name;private double salary;public Teacher() {}public Teacher(String name, double salary) {this.name = name;this.salary = salary;}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getSalary() {return salary;}public void setSalary(double salary) {this.salary = salary;} }
利用反射动态的创建对象和运行方法
package a06_综合练习.a02_利用反射动态的创建对象和运行方法;import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Properties;public class Test {public static void main(String[] args) throws InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException, ClassNotFoundException, IOException {/*反射可以跟配置文件结合的方式,动态的创建对象,并调用方法*///1.读取配置文件中的信息Properties prop = new Properties();FileInputStream fis = new FileInputStream("a26_反射\\prop.properties");prop.load(fis);fis.close();System.out.println(prop);//2.获取全类名和方法名String className = (String) prop.get("classname");String methodName = (String) prop.get("method");System.out.println(className);System.out.println(methodName);//3.利用反射创建对象并运行方法Class clazz = Class.forName(className);//获取构造方法Constructor con = clazz.getDeclaredConstructor();Object o = con.newInstance();System.out.println(o);//获取成员方法并运行Method method = clazz.getDeclaredMethod(methodName);method.setAccessible(true);method.invoke(o);} }
package a06_综合练习.a02_利用反射动态的创建对象和运行方法;public class Teacher {private String name;private double salary;public Teacher() {}public Teacher(String name, double salary) {this.name = name;this.salary = salary;}public void teach() {System.out.println("老师在教书!");}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getSalary() {return salary;}public void setSalary(double salary) {this.salary = salary;} }
package a06_综合练习.a02_利用反射动态的创建对象和运行方法;public class Student {private String name;private int age;public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}public void study(){System.out.println("学生在学习!");}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;} }
classname=a06_综合练习.a02_利用反射动态的创建对象和运行方法.Teacher method=teach
反射总结:
1.反射的作用获取任意一个类中的所有信息结合配置文件动态创建对象 2.获得 class 字节码文件对象的三种方式Class.forName ("全类名");类名.class对象.getClass (); 3.如何获取构造方法、成员方法、成员变量get:获取Constructor:构造方法Field:成员变量Method:方法set:设置Parameter:参数Modifiers:修饰符Declared:私有的
动态代理
思路分析
程序为什么需要代理?代理长什么样?对象如果嫌身上干的事太多的话,可以通过代理来转移部分职责对象有什么方法想被代理,代理就一定要有对应的方法 中介如何知道要派有唱歌、跳舞方法的代理呢?接口1.为什么需要代理?代理可以无侵入式的给对象增强其他的功能调用者——>代理——>对象 2.代理长什么样?代理里面就是对象要被代理的方法 3.Java通过什么来保证代理的样子?通过接口保证,后面的对象和代理需要实现同一个接口接口中就是被代理的所有方法
代码实现
package a02_代码实现;public class Test {public static void main(String[] args) {/*需求:外面的人想要大明星唱一首歌1. 获取代理的对象代理对象 = ProxyUtil.createProxy(大明星的对象);2. 再调用代理的唱歌方法代理对象.唱歌的方法("只因你太美");*///1. 获取代理的对象BigStar bigStar = new BigStar("鸡哥");Star proxy = ProxyUtil.createProxy(bigStar);//2. 调用唱歌的方法String result = proxy.sing("只因你太美");System.out.println(result);//3. 调用跳舞的方法proxy.dance();} }
package a02_代码实现;public class BigStar implements Star {private String name;public BigStar() {}public BigStar(String name) {this.name = name;}//唱歌@Overridepublic String sing(String name) {System.out.println(this.name + "正在唱" + name);return "谢谢";}//跳舞@Overridepublic void dance() {System.out.println(this.name + "正在跳舞");}public String getName() {return name;}public void setName(String name) {this.name = name;} }
package a02_代码实现;import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;public class ProxyUtil { // 方法的作用: // 给一个明星的对象,创建一个代理 // 形参: // 被代理的明星对象 // 返回值: // 给明星创建的代理// 需求: // 外面的人想要大明星唱一首歌 // 1.获取代理的对象 // 代理对象 = ProxyUtil.createProxy (大明星的对象); // 2.再调用代理的唱歌方法 // 代理对象.唱歌的方法("只因你太美");public static Star createProxy(BigStar bigStar){/* java.lang.reflect.Proxy类: 提供了为对象产生代理对象的方法:public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)参数一: 用于指定用哪个类加载器,去加载生成的代理类参数二: 指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法参数三: 用来指定生成的代理对象要干什么事情*/Star star = (Star) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),//参数一: 用于指定用哪个类加载器,去加载生成的代理类new Class[]{Star.class},//参数二: 指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法//参数三: 用来指定生成的代理对象要干什么事情new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {/** 参数一: 代理的对象* 参数二: 要运行的方法 sing* 参数三: 调用sing方法时,传递的实参* */if("sing".equals(method.getName())){System.out.println("准备话筒,收钱");}else if("dance".equals(method.getName())){System.out.println("准备场地,收钱");}//去找大明星开始唱歌或者跳舞//代码的表现形式: 调用大明星里面唱歌或者跳舞的方法return method.invoke(bigStar,args);}});return star;} }
package a02_代码实现;public interface Star {//我们可以把所有想要被代理的方法定义在接口当中//唱歌public abstract String sing(String name);//跳舞public abstract void dance(); }