多线程基础
线程:一个程序内部的一条执行流程,只有一条执行流程就是单线程
java.lang.Thread代表线程
主线程退出,子线程存在,进程不会退出
可以使用jconsole
查看
创建线程
有多个方法可以创建线程
-
继承Thread类
- 优点:编码简单
- 缺点:无法继承其他类,不利于功能的扩展
-
实现Runnable接口
- 优点:任务类只是实现了接口,可以继续继承其他类、实现其他接口,扩展性强
- 缺点:需要多创建一个Runnable对象
-
实现Callable接口和FutureTask类
- 优点:可以返回线程执行结束之后的结果
- 缺点:编码复杂
执行为什么是start()?
使用run不是多线程, 相当于直接调用方法 还是单线程
start->start0(本地方法 JVM调用 C/C++实现的)
方法一
public class Demo1 {public static void main(String[] args) throws Exception {//main是主线程执行的//新建了一个t线程Thread t = new primeThread();//启动线程 start自动调用run方法 必须要调用start方法//如果是t.run() 相当于直接调用方法 还是单线程t.start();for (int i = 0; i < 5; i++) {System.out.println("主线程");Thread.sleep(500);}}
}class primeThread extends Thread{public primeThread(){}@Overridepublic void run() {//描述线程的执行的任务for (int i = 0; i < 5; i++) {System.out.println("子线程");try {Thread.sleep(500);} catch (Exception e) {e.printStackTrace();}}}
}
方法二
public class Demo2 {public static void main(String[] args) throws Exception {//runnable只是一个任务对象Runnable target = new prime1Thread();//需要线程对象接受任务对象 开辟新的线程new Thread(target).start();for (int i = 0; i < 5; i++) {System.out.println("主线程");Thread.sleep(500);}}
}class prime1Thread implements Runnable{@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println("子线程");try {Thread.sleep(500);} catch (Exception e) {e.printStackTrace();}}}
}//可以使用匿名内部类
public class Demo2 {public static void main(String[] args) throws Exception {//需要线程对象进行调用任务对象开辟新的线程new Thread(()-> {for (int i = 0; i < 5; i++) {System.out.println("子线程");try {Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);} }}).start();for (int i = 0; i < 5; i++) {System.out.println("主线程");Thread.sleep(500);}}
}
方法三
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class Demo3 {public static void main(String[] args) throws ExecutionException, InterruptedException {//创建一个Callable对象Callable<String> myCallable = new MyCallable(100);// 把Callable的对象封装成一个FutureTask对象(任务对象)// 未来任务对象的作用?// 1、是一个任务对象,实现下Runnable对象// 2、可以在线程执行完毕之后,用未来任务对象调用get方法获取线程执行完毕的结果//也可以使用匿名内部类FutureTask<String> stringFutureTask = new FutureTask<>(myCallable);new Thread(stringFutureTask).start();//获取结果会阻塞线程System.out.println(stringFutureTask.get());}
}//泛型
class MyCallable implements Callable<String>{private int n;public MyCallable(int n) {this.n = n;}@Overridepublic String call() throws Exception {int sum = 0;for (int i = 1; i <= n; i++) {sum+=i;}return sum+"";}
}
线程方法
setPriority()
更改线程的优先级getPriority()
获取线程的优先级interrupt
中断线程,并不是真正的结束线程 所以一般用于中断正在休眠的线程yield
线程的礼让,不一定礼让成功(和join
相反,线程的插队)
public class Demo4 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread1("1号线程");
// t1.setName("1号线程");//启动之前取名字t1.start();t1.join();
// System.out.println(t1.getName());Thread t2 = new Thread1("2号线程");
// t2.setName("2号线程");//启动之前取名字t2.start();t2.join();//t2线程执行完成之后才能继续往下执行
// System.out.println(t2.getName());Thread t3 = new Thread1("3号线程");t3.start();t3.join();Thread m = Thread.currentThread();m.setName("最牛逼的名字");
// System.out.println(m.getName());for (int i = 0; i < 5; i++) {System.out.println(m.getName()+"输出"+(i+1));}}
}class Thread1 extends Thread{public Thread1(String name) {super(name);}@Overridepublic void run() {Thread t= Thread.currentThread();for (int i = 0; i < 3; i++) {System.out.println("子线程"+t.getName()+"输出:"+(i+1));}}
}
线程终止
- 当线程执行完成时,自动退出
- 使用变量来控制run方法退出的方式停止线程
守护线程
当所有的用户线程都退出时,守护线程自动退出
垃圾回收机制
public class Test {public static void main(String[] args) {//子线程设置为守护线程myDaemonThread myDaemonThread = new myDaemonThread();myDaemonThread.setDaemon(true);myDaemonThread.start();for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName() + " 执行");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}/*
守护线程:
当用户线程退出后 子线程也自动退出*/
class myDaemonThread extends Thread {@Overridepublic void run() {while (true) {System.out.println(Thread.currentThread().getName() + " 正在执行");try {Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
线程安全
概念
多个线程同时操作同一个共享资源的时候可能出现业务安全问题
模拟线程安全问题
package Thread_;public class Demo5 {public static void main(String[] args) {Thread xiaoHong = new DrawThread("小红");Thread xiaoMing = new DrawThread("小明");xiaoMing.start();xiaoHong.start();}
}
class Account{private static double moneys = 100000;private Account(){}public static double getMoneys() {return moneys;}public static void setMoneys(double moneys) {Account.moneys = moneys;}public static boolean drawMoneys(double moneys){String name = Thread.currentThread().getName();if (moneys>Account.getMoneys()){System.out.println(name+"来取钱,钱不够");return false;}Account.moneys-=moneys;System.out.println(name+"来取钱,取钱成功,剩余"+Account.moneys);return true;}
}class DrawThread extends Thread{public DrawThread(String name) {super(name);}@Overridepublic void run() {Account.drawMoneys(100000.0);}
}
线程同步
认识线程同步
多个线程实现先后依次访问共享资源
**加锁:**每次只允许一个线程加锁,加锁之后才能访问,访问完毕之后自动解锁,然后其他线程才能再加锁继续
方法一:同步代码块
把访问共享资源的核心代码给上锁,保证线程安全
synchronized(同步锁){访问共享资源的核心代码
}
对于当前同时执行的线程来说,同步锁必须是同一把(同一对象)
锁对象的选择:
- 实例对象:使用
this
- 静态对象:使用
类型.class
public class Demo5 {public static void main(String[] args) throws InterruptedException {Account acc1 = new Account(100000);Thread xiaoHong = new DrawThread("小红",acc1);Thread xiaoMing = new DrawThread("小明",acc1);xiaoMing.start();xiaoHong.start();Account acc2 = new Account(100000);Thread daGang = new DrawThread("大纲",acc2);Thread daLi = new DrawThread("大力",acc2);daGang.start();daLi.start();}
}class Account {private double moneys;public Account() {}public Account(double moneys) {this.moneys = moneys;}public double getMoneys() {return moneys;}public void setMoneys(double moneys) {this.moneys = moneys;}public void drawMoneys(double moneys) throws InterruptedException {String name = Thread.currentThread().getName();/** 两个人同时竞争lock这个对象(这把锁),只有一个人能够得到* 上锁之后另外一个人要等待开锁** 但是这个lock对于所有的对象是一个锁* 一个对象上锁的时候 和该对象无关的对象也无法进入核心代码* 非static建议使用 this* static建议使用 ClassName.class* */synchronized (this) {
// Thread.sleep(5000); 测试if (moneys > this.getMoneys()) {System.out.println(name + "来取钱,钱不够");} else {this.moneys -= moneys;System.out.println(name + "来取钱,取钱" + moneys + "成功,剩余" + this.moneys);}}}
}class DrawThread extends Thread {private Account acc;public DrawThread(String name,Account acc) {super(name);this.acc = acc;}@Overridepublic void run() {try {acc.drawMoneys(100000);} catch (InterruptedException e) {throw new RuntimeException(e);}}
}
方法二:同步方法
访问共享资源的核心方法给上锁
修饰符 synchronized 返回值类型 方法名称(形参列表){操作共享资源的代码
}
public class Demo5 {public static void main(String[] args) throws InterruptedException {Account acc1 = new Account(100000);Thread xiaoHong = new DrawThread("小红", acc1);Thread xiaoMing = new DrawThread("小明", acc1);xiaoMing.start();xiaoHong.start();Account acc2 = new Account(100000);Thread daGang = new DrawThread("大纲", acc2);Thread daLi = new DrawThread("大力", acc2);daGang.start();daLi.start();}
}class Account {private double moneys;public Account() {}public Account(double moneys) {this.moneys = moneys;}public double getMoneys() {return moneys;}public void setMoneys(double moneys) {this.moneys = moneys;}/*有一个隐含的锁 实例方法是 this 静态方法是 类型.class*/public synchronized void drawMoneys(double moneys) throws InterruptedException {String name = Thread.currentThread().getName();if (moneys > this.getMoneys()) {System.out.println(name + "来取钱,钱不够");} else {this.moneys -= moneys;System.out.println(name + "来取钱,取钱" + moneys + "成功,剩余" + this.moneys);}}
}class DrawThread extends Thread {private Account acc;public DrawThread(String name, Account acc) {super(name);this.acc = acc;}@Overridepublic void run() {try {acc.drawMoneys(100000);} catch (InterruptedException e) {throw new RuntimeException(e);}}
}
方法三:Lock锁
Lock锁是IDK5开始提供的一个新的锁定操作,通过它可以创建出锁对象进行加锁和解锁,更灵活、更方便、更强大
Lock是接口,不能直接实例化,可以采用它的实现类**ReentrantLock
**来构建Lock锁对象。
package Thread_;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class Demo5 {public static void main(String[] args) throws InterruptedException {Account acc1 = new Account(100000);Thread xiaoHong = new DrawThread("小红", acc1);Thread xiaoMing = new DrawThread("小明", acc1);xiaoMing.start();xiaoHong.start();Account acc2 = new Account(100000);Thread daGang = new DrawThread("大纲", acc2);Thread daLi = new DrawThread("大力", acc2);daGang.start();daLi.start();}
}class Account {/*创建了一个锁对象 每一个账户都有一个自己的锁对象不允许二次赋值*/private final Lock lk = new ReentrantLock();private double moneys;public Account() {}public Account(double moneys) {this.moneys = moneys;}public double getMoneys() {return moneys;}public void setMoneys(double moneys) {this.moneys = moneys;}public void drawMoneys(double moneys) throws InterruptedException {String name = Thread.currentThread().getName();try {lk.lock();if (moneys > this.getMoneys()) {System.out.println(name + "来取钱,钱不够");} else {this.moneys -= moneys;System.out.println(name + "来取钱,取钱" + moneys + "成功,剩余" + this.moneys);}} catch (Exception e) {throw new RuntimeException(e);} finally {lk.unlock();//无论try中代码是否有错误 都会解锁}}
}class DrawThread extends Thread {private Account acc;public DrawThread(String name, Account acc) {super(name);this.acc = acc;}@Overridepublic void run() {try {acc.drawMoneys(100000);} catch (InterruptedException e) {throw new RuntimeException(e);}}
}
释放锁的时机
- 当前线程的同步方法、同步代码块执行结束
- 当前线程在同步方法、同步代码块中遇到
break
、return
- 当前线程在同步方法、同步代码块中出现了未处理的
Error
或者Exception
,导致异常结束 - 当前线程在同步方法、同步代码块中执行了线程对象的
wait()
方法,当前线程暂停 释放锁,等待唤醒
不释放锁
Thread.sleep()
、Thread.yeild
不会释放锁suspend()
挂起方法,也不会释放锁suspend
、resume
控制线程,不推荐使用
线程死锁
多个线程都占用了对方的锁资源,但是不肯相让,导致了死锁
public class Demo {public static void main(String[] args) {new Thread(new MyDeadThread(false)).start();new Thread(new MyDeadThread(true)).start();}
}
class MyDeadThread implements Runnable{private boolean flag;private static Object o1 = new Object();private static Object o2 = new Object();public MyDeadThread() {}public MyDeadThread(boolean flag) {this.flag = flag;}@Overridepublic void run() {while (true){/*flag=true 占用o1锁 抢夺o2锁flag=false 占用o2锁 抢夺o1锁如果两个线程 一个占用o1 一个占用o2 那么就造成死锁*/if (flag){synchronized (o1){System.out.println("o1");synchronized (o2){System.out.println("o2");}}}else {synchronized (o2){System.out.println("o2");synchronized (o1){System.out.println("o1");}}}}}
}
线程通信
当多个线程共同操作共享资源的时候,线程间通过某种方式相互告知自己的状态,相互协调,避免无效的资源争夺
生产者消费者模型
- 生产者线程负责生产数据
- 消费者线程负责消费生产者生产的数据
- 生产者生产完数据应该等待,通知消费者消费;消费者消费完数据也应该等待,通知生产者生产
public class ThreadTest {public static void main(String[] args) {Desk desk = new Desk();//3个生产者new Thread(()-> {while (true){desk.put();}},"厨师1").start();new Thread(()-> {while (true){desk.put();}},"厨师2").start();new Thread(()-> {while (true){desk.put();}},"厨师3").start();//2个消费者new Thread(()-> {while (true){desk.get();}},"吃货1").start();new Thread(()-> {while (true){desk.get();}},"吃货2").start();}
}import java.util.ArrayList;
import java.util.List;public class Desk {private final List<String>list = new ArrayList<>();//这个五个人是同一把锁public synchronized void put(){try {String name = Thread.currentThread().getName();if (list.isEmpty()){list.add(name+"做的肉包子");System.out.println(name+"做的肉包子");Thread.sleep(500);}//等待自己 唤醒别人 先唤醒后等待//只能线程对象调用this.notify();this.wait();} catch (Exception e) {throw new RuntimeException(e);}}public synchronized void get(){try {String name = Thread.currentThread().getName();if (!list.isEmpty()){System.out.println(name + "吃了"+list.remove(0));Thread.sleep(500);}//等待自己 唤醒别人 先唤醒后等待//只能线程对象调用this.notify();this.wait();} catch (Exception e) {throw new RuntimeException(e);}}
}
线程池
概念
可以复用线程的技术
**不使用线程池:**用户每发起一个请求,后台就需要创建一个新线程来处理,下次新任务来了肯定又要创建新线程处理的,而创建新线程的开销是很大的,并且请求过多时,肯定会产生大量的线程出来,这样会严重影响系统的性能。
使用ExecutorService
创建线程池
使用ExecutorService
的实现类ThreadPoolExecutor
创建一个线程池对象(JDK5.0之后提供代表线程池的接口:ExecutorService
)
corePoolSize
:指定线程池的核心线程的数量maximumPoolSize
:指定线程池的最大线程的数量keepAliceTime
:指定临时线程的存活时间unit
:指定临时线程存货时间的单位(秒、分、时、天)workQueue
:指定线程池的任务队列threadFactory
:指定线程池的线程工厂handler
:指定线程池的任务拒绝策略(线程都在忙,任务队列也满了的时候,新任务来了该怎么处理)
/*ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)*/ExecutorService poolExecutor = new ThreadPoolExecutor(3, 5, 8, TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
什么时候创建临时对象?
新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,才会创建
什么时候会开始拒绝新任务?
核心线程和临时线程都在忙,任务队列也满了
新任务拒绝策略
处理Runnable任务
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class _ThreadPool {public static void main(String[] args) {/*ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)*/ExecutorService poolExecutor = new ThreadPoolExecutor(3, 5,8, TimeUnit.SECONDS, new ArrayBlockingQueue<>(4),Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());MyRunnable myRunnable1 = new MyRunnable();MyRunnable myRunnable2 = new MyRunnable();MyRunnable myRunnable3 = new MyRunnable();//三个核心线程在忙poolExecutor.execute(myRunnable1);poolExecutor.execute(myRunnable2);poolExecutor.execute(myRunnable3);//任务队列占满poolExecutor.execute(myRunnable3);poolExecutor.execute(myRunnable3);poolExecutor.execute(myRunnable3);poolExecutor.execute(myRunnable3);//可以创建两个临时线程poolExecutor.execute(myRunnable3);poolExecutor.execute(myRunnable3);//拒绝新任务poolExecutor.execute(myRunnable3);// poolExecutor.shutdown();//等任务执行完后关闭线程池
// poolExecutor.shutdownNow();//立刻关闭线程池}
}class MyRunnable implements Runnable {@Overridepublic void run() {String name = Thread.currentThread().getName();System.out.println(name + "666");try {Thread.sleep(100000);} catch (InterruptedException e) {throw new RuntimeException(e);}}
}
处理Callable任务
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class _ThreadPool {public static void main(String[] args) throws Exception {/*ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)*/ExecutorService poolExecutor = new ThreadPoolExecutor(3, 5,8, TimeUnit.SECONDS, new ArrayBlockingQueue<>(4),Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());Future<String>f1 = poolExecutor.submit(new MyCallable(100));Future<String>f2 = poolExecutor.submit(new MyCallable(200));Future<String>f3 = poolExecutor.submit(new MyCallable(300));Future<String>f4 = poolExecutor.submit(new MyCallable(400));System.out.println(f1.get());System.out.println(f2.get());System.out.println(f3.get());System.out.println(f4.get());}
}class MyCallable implements Callable<String> {private int n;public MyCallable(int n) {this.n = n;}@Overridepublic String call() throws Exception {int sum = 0;for (int i = 1; i <= n; i++) {sum+=i;}return Thread.currentThread().getName()+"计算出1-"+n+"的和为"+sum;}
}
使用Executors
创建线程池(大型并发系统不建议)
(线程池的工具类)调用方法返回不同特点的线程池对象
- FixedThreadPool、SingleThreadExecutor允许请求队列长度为Integer.MAX_VALUE
- CachedThreadPool允许创建线程数量为Integer.MAX_VALUE
这些方法的底层,都是通过线程池的实现类ThreadPoolExecutor创建的线程池对象
ExecutorService pool = Executors.newFixedThreadPool(3);
核心线程配置数量
- 计算密集型的任务:CPU核数+1
- IO密集型的任务:CPU核数*2
并发和并行
并发的含义
进程中的线程是由CPU负责调度执行的,但是CPU能同时处理线程的数量是有限的。
为了保证全部线程都能往前执行,CPU会轮询为系统的每个线程服务,由于CPU切换速度很快,给我们的感觉就是这些线程在同时执行,这就是并发
并行的含义
同一时刻上,同时有多个线程在被CPU调度执行
线程生命周期
也就是线程从生到死的过程,经历的各种状态以及状态转换
理解线程这些状态有利于提高并发编程的理解能力
扩展:悲观锁和乐观锁
悲观锁:一开始就加锁,没有安全感,每次只能一个线程进入,访问完毕后再解锁。线程安全 性能较差
乐观锁:一开始不上锁,认为没问题,等出现线程安全的时候才开始控制。线程安全 性能较好
//乐观锁
import java.util.concurrent.atomic.AtomicInteger;public class Demo7 {//一个静态变量,100个线程,每个线程对其加100次public static void main(String[] args) {Runnable mRunnable = new MRunnable2();for (int i = 0; i < 100; i++) {//100个线程执行相同的任务new Thread(mRunnable).start();}}
}class MRunnable2 implements Runnable {
// private int count;//整数修改的乐观锁:原子类,private AtomicInteger count = new AtomicInteger();@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println("count====>" + (count.incrementAndGet()));}}
}
多线程练习
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;public class Test1 {public static void main(String[] args) throws InterruptedException {/*** 目标:有100份礼品,小红,小明两人同时发送,当剩下的礼品小于10份的时候则不再送出,* 利用多线程模拟该过程并将线程的名称打印出来。并最后在控制台分别打印小红,小明各自送出多少分礼物。*/ArrayList<String> gifts = new ArrayList<>();String[] names = {"口红", "包包", "腰带", "剃须刀", "香水", "衣服"};Random r = new Random();for (int i = 0; i < 100; i++) {gifts.add(names[r.nextInt(names.length)] + (i + 1));}sendThread xm = new sendThread(gifts, "小明");sendThread xh = new sendThread(gifts, "小红");xm.start();xh.start();xm.join();xh.join();System.out.println("小明送出去" + xm.getCount());System.out.println("小红送出去" + xh.getCount());}
}class sendThread extends Thread {private ArrayList<String> gifts;private int count;public int getCount() {return count;}public void setCount(int count) {this.count = count;}public sendThread() {}public sendThread(ArrayList<String> gifts, String name) {super(name);this.gifts = gifts;}@Overridepublic void run() {Random r = new Random();String name = Thread.currentThread().getName();while (true) {synchronized (gifts) {int length = gifts.size();if (length < 10)break;String s = gifts.remove(r.nextInt(length));System.out.println(name + "送出礼物" + s);++count;}}}
}
网络编程基础
可以让设备中的程序与网络上其他设备中的程序进行数据交互(实现网络通信的)
java.net.*
的包下
网络通信三要素
- IP地址:设备在网络中的地址,是唯一的标识
- 端口号:应用程序在设备中唯一的标识
- 协议:连接和数据在网络中传输的规则
java获取Ip地址:InetAddress
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/fe8b9baf7b3b42619311c8ac4ff54acf.png)
import java.net.InetAddress;
import java.net.UnknownHostException;public class GetIP {public static void main(String[] args) throws Exception {//本机InetAddress ip = InetAddress.getLocalHost();System.out.println(ip.getHostName());System.out.println(ip.getHostAddress());//指定InetAddress ipBaiDu = InetAddress.getByName("www.baidu.com");System.out.println(ipBaiDu.getHostName());System.out.println(ipBaiDu.getHostAddress());//本机ping 百度System.out.println(ipBaiDu.isReachable(6000));}
}
UDP通信
java.net.DatagramSocket
实现UDP通信
一发一收
Client
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;public class Client {public static void main(String[] args) throws Exception {//创建客户端 以及客户端端口DatagramSocket socket = new DatagramSocket(6666);String data = "我是客户端,哈哈哈";byte[]bytes = data.getBytes();//创建数据包DatagramPacket packet = new DatagramPacket(bytes,bytes.length, InetAddress.getLocalHost(),5555);//发送数据socket.send(packet);System.out.println("客户端数据发送完毕");//释放资源socket.close();}
}
Serve
import java.net.DatagramPacket;
import java.net.DatagramSocket;public class Serve {public static void main(String[] args) throws Exception {System.out.println("===服务端启动===");//创建服务端 注册服务端端口DatagramSocket socket = new DatagramSocket(5555);byte[] buffer = new byte[1024*64];//64KB UDP一个数据包最大为64KB//创建一个用来接收数据的数据包对象DatagramPacket packet = new DatagramPacket(buffer, buffer.length);//接受数据socket.receive(packet);//从字节数组中获取接受的数据int len = packet.getLength();String data = new String(buffer,0,len);System.out.println(data);//获取客户端的IP 端口System.out.println(packet.getAddress().getHostAddress());System.out.println(packet.getPort());//释放资源socket.close();}
}
多发多收
可以多个用户同时发送
Client
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;public class Client {public static void main(String[] args) throws Exception {//创建客户端 以及客户端端口(默认随机分配)DatagramSocket socket = new DatagramSocket();Scanner sc = new Scanner(System.in);while (true) {System.out.println("请输入消息://exit是退出");String msg = sc.nextLine();if (msg.equals("exit")){System.out.println("欢迎下次光临");break;}byte[]bytes = msg.getBytes();//创建数据包DatagramPacket packet = new DatagramPacket(bytes,bytes.length, InetAddress.getLocalHost(),5555);//发送数据socket.send(packet);}socket.close();}
}
Serve
import java.net.DatagramPacket;
import java.net.DatagramSocket;public class Serve {public static void main(String[] args) throws Exception {System.out.println("===服务端启动===");//创建服务端 注册服务端端口DatagramSocket socket = new DatagramSocket(5555);byte[] buffer = new byte[1024*64];//64KB UDP一个数据包最大为64KB//创建一个用来接收数据的数据包对象DatagramPacket packet = new DatagramPacket(buffer, buffer.length);while (true) {//接受数据socket.receive(packet);//从字节数组中获取接受的数据int len = packet.getLength();String data = new String(buffer,0,len);System.out.println(data);//获取客户端的IP 端口System.out.println(packet.getAddress().getHostAddress());System.out.println(packet.getPort());System.out.println("----------------");}}
}
TCP通信
客户端:java.net.Socket
一发一收
client
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;public class ClientTCP {public static void main(String[] args) throws Exception {//创建socket对象Socket socket = new Socket(InetAddress.getLocalHost(), 5555);//从socket通信管道中得到一个字节输出流OutputStream os = socket.getOutputStream();//封装成数据输出流DataOutputStream dataOutputStream = new DataOutputStream(os);//写入数据dataOutputStream.writeUTF("你好呀!");//关闭数据流dataOutputStream.close();//关闭socketsocket.close();}
}
serve
import java.io.DataInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;public class ServeTCP {public static void main(String[] args) throws Exception{System.out.println("--服务端启动--");//创建服务端对象 绑定端口ServerSocket serverSocket = new ServerSocket(5555);//等待连接Socket socket = serverSocket.accept();//接受数据InputStream ds = socket.getInputStream();//封装DataInputStream dataInputStream = new DataInputStream(ds);//接受数据String s = dataInputStream.readUTF();System.out.println(s);//客户端ip地址System.out.println(socket.getRemoteSocketAddress());dataInputStream.close();socket.close();}
}
多发多收
client
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Objects;
import java.util.Scanner;public class ClientTCP {public static void main(String[] args) throws Exception {//创建socket对象Socket socket = new Socket(InetAddress.getLocalHost(), 5555);//从socket通信管道中得到一个字节输出流OutputStream os = socket.getOutputStream();//封装成数据输出流DataOutputStream dataOutputStream = new DataOutputStream(os);Scanner sc = new Scanner(System.in);while (true) {//写入数据System.out.println("请说:");String s = sc.nextLine();if (Objects.equals(s, "exit")){System.out.println("欢迎下次光临");break; }dataOutputStream.writeUTF(s);dataOutputStream.flush();}//关闭数据流dataOutputStream.close();//关闭socketsocket.close();}
}
serve
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;public class ServeTCP {public static void main(String[] args) throws Exception{System.out.println("--服务端启动--");//创建服务端对象 绑定端口ServerSocket serverSocket = new ServerSocket(5555);//等待连接Socket socket = serverSocket.accept();//接受数据InputStream ds = socket.getInputStream();//封装DataInputStream dataInputStream = new DataInputStream(ds);//接受数据while (true) {try {String s = dataInputStream.readUTF();System.out.println(s);//客户端ip地址
// System.out.println(socket.getRemoteSocketAddress());} catch (IOException e) {System.out.println(socket.getRemoteSocketAddress()+"离线");break;}}dataInputStream.close();socket.close();}
}
多个客户端连接一个服务端
服务端:
- 主线程负责接受客户端连接
- 子线程负责具体每一个客户端
client
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;public class ClientTCP {public static void main(String[] args) throws Exception {Socket socket = new Socket("127.0.0.1", 8888);OutputStream os = socket.getOutputStream();DataOutputStream dos = new DataOutputStream(os);Scanner sc = new Scanner(System.in);while (true){String s = sc.nextLine();if (s.equals("exit")){System.out.println("欢迎下次光临");dos.close();socket.close();break;}dos.writeUTF(s);dos.flush();}}
}
serve
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;public class ServeTCP {public static void main(String[] args) throws Exception {System.out.println("服务端开启...");ServerSocket serverSocket = new ServerSocket(8888);while (true) {Socket socket = serverSocket.accept();System.out.println(socket.getRemoteSocketAddress()+"上线了");new Thread(new SocketThread(socket)).start();}}
}class SocketThread implements Runnable{private Socket socket;public SocketThread(Socket socket){this.socket = socket;}@Overridepublic void run() {SocketAddress remoteSocketAddress = socket.getRemoteSocketAddress();try {InputStream is = socket.getInputStream();DataInputStream dis = new DataInputStream(is);while (true) {try {String s = dis.readUTF();System.out.println(remoteSocketAddress+"发送:"+s);} catch (Exception e) {System.out.println(remoteSocketAddress+"下线了");socket.close();dis.close();break;}}} catch (Exception e) {e.printStackTrace();}}
}
案例:群聊
client
import java.io.DataInputStream;
import java.io.DataOutputStream;import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;public class ClientChat {public static void main(String[] args) {try {Socket socket = new Socket("127.0.0.1", 8888);new ClientThread(socket).start();OutputStream os = socket.getOutputStream();DataOutputStream dos = new DataOutputStream(os);Scanner sc = new Scanner(System.in);while (true) {String s = sc.nextLine();if (s.equals("exit")) {System.out.println("欢迎下次光临");socket.close();dos.close();break;}dos.writeUTF(s);dos.flush();}} catch (Exception e) {e.printStackTrace();}}
}class ClientThread extends Thread {private Socket socket;public ClientThread(Socket socket) {this.socket = socket;}@Overridepublic void run() {try {InputStream is = socket.getInputStream();DataInputStream dis = new DataInputStream(is);while (true) {try {String msg = dis.readUTF();System.out.println(msg);} catch (Exception e) {dis.close();socket.close();break;}}} catch (Exception e) {e.printStackTrace();}}
}
serve
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;public class ServeChat {public static final List<Socket> onlineUsers = new ArrayList<>();public static void main(String[] args) throws Exception{System.out.println("==服务器启动==");ServerSocket serverSocket = new ServerSocket(8888);while (true) {Socket socket = serverSocket.accept();onlineUsers.add(socket);new ServeReaderThread(socket).start();}}
}class ServeReaderThread extends Thread {private Socket socket;public ServeReaderThread(Socket socket) {this.socket = socket;}@Overridepublic void run() {try {InputStream is = socket.getInputStream();DataInputStream dis = new DataInputStream(is);while (true) {try {String msg = dis.readUTF();System.out.println(msg);sendAllOnlineUsers(socket,msg);} catch (Exception e) {ServeChat.onlineUsers.remove(socket);socket.close();dis.close();System.out.println(socket.getRemoteSocketAddress() + "下线");break;}}} catch (Exception e) {e.printStackTrace();}}private void sendAllOnlineUsers(Socket socket,String msg) throws Exception {for (Socket onlineUser : ServeChat.onlineUsers) {SocketAddress remoteSocketAddress = socket.getRemoteSocketAddress();if (Objects.equals(onlineUser.getRemoteSocketAddress(),remoteSocketAddress)){continue;}OutputStream os = onlineUser.getOutputStream();DataOutputStream dos = new DataOutputStream(os);dos.writeUTF(remoteSocketAddress+"说:"+msg);dos.flush();}}
}
案例:简易BS架构
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;public class Serve {public static void main(String[] args) throws Exception {ServerSocket serverSocket = new ServerSocket(8080);while (true){Socket socket = serverSocket.accept();System.out.println(socket.getRemoteSocketAddress()+"上线了");new CThread(socket).start();}}
}class CThread extends Thread{private Socket socket;public CThread(Socket socket){this.socket=socket;}@Overridepublic void run() {try {OutputStream os = socket.getOutputStream();PrintStream ps = new PrintStream(os);/*服务器必须给浏览器相应Http协议规定的格式*/ps.println("HTTP/1.1 200 OK");ps.println("Content-Type:text/html;charset=UTF-8");ps.println();//必须换行ps.println("<div style='color:red;font-size:120px;'>java666</div>");ps.close();socket.close();} catch (Exception e) {e.printStackTrace();}}
}
改进:线程池
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
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 Serve {public static void main(String[] args) throws Exception {ServerSocket serverSocket = new ServerSocket(8080);ThreadPoolExecutor pool = new ThreadPoolExecutor(16 * 2, 16 * 2, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(8), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());while (true){Socket socket = serverSocket.accept();System.out.println(socket.getRemoteSocketAddress()+"上线了");pool.execute(new CThread(socket));}}
}class CThread implements Runnable{private Socket socket;public CThread(Socket socket){this.socket=socket;}@Overridepublic void run() {try {OutputStream os = socket.getOutputStream();PrintStream ps = new PrintStream(os);/*服务器必须给浏览器相应Http协议规定的格式*/ps.println("HTTP/1.1 200 OK");ps.println("Content-Type:text/html;charset=UTF-8");ps.println();//必须换行ps.println("<div style='color:red;font-size:120px;'>java666</div>");ps.close();socket.close();} catch (Exception e) {e.printStackTrace();}}
}
案例:多用户即时通信系统
需求分析
- 用户登录
- 拉取在线用户
- 无异常退出
- 私聊
- 群聊
- 发文件
- 服务器推送新闻
java高级
单元测试
就是针对最小的功能单元(方法),编写测试代码对其进行正确性测试
junit单元测试框架
- 可以灵活的编写测试代码,可以针对某个方法执行测试,也支持一键完成对全部的方法自动化测试
- 不需要程序员去分析测试结果,会自动生成测试报告
具体使用
public class Demo {public static void printNumber(String name){if (name==null)return;System.out.println("名字长度:"+name.length());}public static int getMaxIndex(String data){if (data==null)return -1;return data.length();}
}
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;/*
测试类*/
public class DemoTest {@Beforepublic void test1(){System.out.println("---------Before---------");}@Afterpublic void test2(){System.out.println("---------After---------");}@AfterClasspublic static void test3(){System.out.println("---------AfterClass---------");}@BeforeClasspublic static void test4(){System.out.println("---------BeforeClass---------");}/*公开 无返回值*/@Test //测试方法public void testPrintNumber(){Demo.printNumber("admin");Demo.printNumber(null);}@Test //测试方法public void testGetMaxIndex(){//断言机制:可以通过预测业务方法的结果来测试 bugSystem.out.println(Demo.getMaxIndex("admin"));System.out.println(Demo.getMaxIndex(null));//断言机制:可以通过预测业务方法的结果来测试 bugAssert.assertEquals("有bug",4,Demo.getMaxIndex("admin"));}
}
以下是学习框架源码的时候会用到,开发几乎不会用
反射
认识反射
加载类,并允许以编程的方式解剖类中的各个成分(成员变量、方法、构造器等)
步骤
- 加载类,获取类的字节码:Class对象
- 获取类的构造器:Constructor对象
- 获取类成员变量:Field对象
- 获取类成员方法:Method对象
获取类的字节码
- Class c1 = 类名.class
- 调用Class提供的方法
public static Class forName(String package);
全类名 - Object的方法 对象.getClass()
获取类的构造器
import java.lang.reflect.Constructor;public class Demo1 {public static void main(String[] args) throws Exception {Class c = Cat.class;Constructor constructor = c.getDeclaredConstructor();System.out.println(constructor.getName()+"--"+constructor.getParameterCount());Cat o = (Cat) constructor.newInstance();System.out.println(o);Constructor declaredConstructor = c.getDeclaredConstructor(String.class, int.class);System.out.println(declaredConstructor.getName()+"--"+declaredConstructor.getParameterCount());declaredConstructor.setAccessible(true);//打破修饰符的限制Cat o1 = (Cat)declaredConstructor.newInstance("学习", 5);}
} class Cat{private String name;private int 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 Cat() {}private Cat(String name, int age) {this.name = name;this.age = age;}public Cat(String name) {this.name = name;}
}
获取类的成员变量
import java.lang.reflect.Field;public class Demo1 {public static void main(String[] args) throws Exception {Class c = Cat.class;Field[] fields = c.getDeclaredFields();for (Field field : fields) {System.out.println(field.getName()+"--"+field.getType());}Field name = c.getDeclaredField("name");System.out.println(name.getName()+"--"+name.getType());Cat cat = new Cat();name.setAccessible(true);name.set(cat,"猫猫");System.out.println(name.get(cat));}
}class Cat{public static int a;public static final String COUNTRY ="中国";private String name;private int 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 Cat() {}private Cat(String name, int age) {this.name = name;this.age = age;}public Cat(String name) {this.name = name;}
}
获取类的成员方法
作用、应用场景
基本作用:可以得到一个类全部成分对其操作;可以破坏封装性;适合做java的框架
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Field;public class Test {public static void main(String[] args)throws Exception {Student stu = new Student("小明", 18, 82.5);Teacher tea = new Teacher("大强", 58);saveObject(stu);saveObject(tea);}public static void saveObject(Object obj) throws Exception {Class o = obj.getClass();String cname = o.getSimpleName();PrintStream ps = new PrintStream(new FileOutputStream("./out/obj.txt",true));ps.println("---------"+cname+"---------");Field[] fields = o.getDeclaredFields();for (Field field : fields) {field.setAccessible(true);String name = field.getName();String value = field.get(obj)+"";ps.println(name+":"+value);}ps.close();}
}
class Student{private String name;private int age;private double sorce;public Student(String name, int age, double sorce) {this.name = name;this.age = age;this.sorce = sorce;}
}class Teacher{private String name;private int age;public Teacher(String name, int age) {this.name = name;this.age = age;}
}
注解
就是java中特殊的标记,比如@override、@Test等
作用:让其他程序根据注解信息来决定怎么执行程序
注解可以用在类、方法、构造器、成员变量、参数等等
自定义注解
public @interface 注解名称{public 属性类型 属性名() default 默认值;
}
只有一个注解 且为 value 可以省略不写value
注解原理
注解本质就是一个接口,java中所有的注解都是继承了Annotation的接口
@注解(…)其实就是一个实现类对象,实现了该注解以及Annotation的接口
元注解
修饰注解的注解
注解的解析
就是判断类上、方法上、成员变量上是否存在注解,并把注解里的内容给解析出来。
要解析谁的注解,就要先拿到谁
Class、Method、Field,Constructor、都实现了AnnotatedElement接口,它们都拥有解析注解的能力。
package annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.TYPE,ElementType.METHOD})//当前被修饰的注解只能使用在类上
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest{String value();double aaa() default 100;String[] bbb();
}
package annotation;@MyTest(value = "大强",aaa = 199.9,bbb={"css","java","html"})public class Demo {
@MyTest(value = "小明",aaa = 99.9,bbb={"java","html"})void test(){}
}
package annotation;import java.lang.reflect.Method;
import java.util.Arrays;public class AnnotationTest {public static void main(String[] args) throws Exception {Class c = Demo.class;Method test = c.getDeclaredMethod("test");if (c.isAnnotationPresent(MyTest.class)) {MyTest myTest = (MyTest) c.getDeclaredAnnotation(MyTest.class);System.out.println(myTest.value());System.out.println(myTest.aaa());System.out.println(Arrays.toString(myTest.bbb()));}if (test.isAnnotationPresent(MyTest.class)) {MyTest myTest = test.getDeclaredAnnotation(MyTest.class);System.out.println(myTest.value());System.out.println(myTest.aaa());System.out.println(Arrays.toString(myTest.bbb()));}}
}
应用场景
模拟junit
package annotation;import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public class TestTest {@MyTest2public void test1(){System.out.println("==test1==");}public void test2(){System.out.println("==test2==");}public void test3(){System.out.println("==test3==");}public static void main(String[] args) throws Exception {Class c = TestTest.class;Method[] methods = c.getDeclaredMethods();for (Method method : methods) {if (method.isAnnotationPresent(MyTest2.class)){method.invoke(new TestTest());}}}
}
动态代理
概念
对象做的事情太多的话,可以通过代理来转移部分职责
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-blog.csdnimg.cn/direct/867cad296ec44d6385e47ae55f719acc.png)
package proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class ProxyUtil {public static Star createProxy(BigStar bigStar) {/*参数1:指定一个类加载器参数2:指定生成的代理是什么样子,也就是有什么方法参数3:指定生成的代理对象要干什么事情*/return (Star) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(), new Class[]{Star.class}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//代理对象要做的事情 会在这里写代码if (method.getName().equals("sing")){System.out.println("准备话筒,收钱20w");}else if (method.getName().equals("dance")){System.out.println("准备场地,收钱1000w");}return method.invoke(bigStar,args);}});}
}
package proxy;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(name+"正在跳舞~~~");}
}
package proxy;public interface Star {public String sing(String name);public void dance();
}
package proxy;public class Test {public static void main(String[] args) {BigStar s = new BigStar("杨超越");Star starProxy = ProxyUtil.createProxy(s);String rs = starProxy.sing("好日子");System.out.println(rs);System.out.println("--------------------------");starProxy.dance();}
}
坦克大战
java坐标系
下图说明了Java坐标系。坐标原点位于左上角,以像素为单位。在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。
package tankeGame;import javax.swing.*;
import java.awt.*;public class Draw extends JFrame {//JFrame 对应窗口 可以理解为一个画框private MyPanel mp =null;//定义一个画板public Draw(){//初始化画板mp = new MyPanel();//画板放入窗口this.add(mp);//设置窗口大小this.setSize(1000,800);//可以显示this.setVisible(true);//点窗口的× 程序退出this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);}public static void main(String[] args) {new Draw();}
}//1.定义一个MyPanel继承JPanel,这个就是画板 画图形
class MyPanel extends JPanel{/*MyPanel:画板(面板)对象Graphics g:画笔paint调用时机:1.组件第一次在屏幕中显示的时候,系统自动调用2.窗口最小化 再最大化3.窗口大小发生变化4.repaint函数被调用*/@Overridepublic void paint(Graphics g) {//绘图的方法super.paint(g);//调用父类的方法完成初始化//画一个圆g.drawOval(10,10,100,100);//画直线g.drawLine(10,10,60,60);//画矩形g.drawRect(10,10,100,100);//填充矩形//设置画笔颜色g.setColor(Color.BLUE);g.fillRect(50,50,100,100);g.fillOval(200,200,50,60);//画图片//1.加载图片资源Image image = Toolkit.getDefaultToolkit().getImage("d:/shangan.png");//2.画图片g.drawImage(image,300,300,300,300,this);//画字符串g.setColor(Color.cyan);g.setFont(new Font("隶书",Font.BOLD,50));//位置是字体的左下角g.drawString("yb0os1",500,100);}
}
事件处理机制
委派事件模型
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;//事件控制 键盘控制小球的移动
//画笔
public class BallMove extends JFrame {private DrawBall ball = null;public BallMove() {ball = new DrawBall();this.add(ball);this.setVisible(true);this.setSize(500, 400);this.addKeyListener(ball);//JFame对象可以监听ball上面发生的键盘事件this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);}public static void main(String[] args) {new BallMove();}
}//画板
//KeyListener 监听器 监听键盘事件
class DrawBall extends JPanel implements KeyListener {int x = 10;int y = 10;@Override//有字符输出时 该方法会触发public void keyTyped(KeyEvent e) {}@Override//当某个键被按下时 该方法会触发public void keyPressed(KeyEvent e) {
// System.out.println((char) e.getKeyChar() + "被按下");//根据用户按下的不同键,来处理小球的移动//java中给每一个键分配一个值switch (e.getKeyCode()){case KeyEvent.VK_DOWN://向下的箭头++y;break;case KeyEvent.VK_UP://向上的--y;break;case KeyEvent.VK_LEFT://向左--x;break;case KeyEvent.VK_RIGHT://向右++x;break;}//重绘面板this.repaint();}@Override//当某个键被松开时 该方法会触发public void keyReleased(KeyEvent e) {}@Overridepublic void paint(Graphics g) {super.paint(g);g.fillOval(x, y, 20, 20);}
}