多线程..

线程定义:线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中实际运作单位。简单来说,应用软件中相互独立,可以同时运作的功能。

多线程作用:有了多线程,我们就可以让程序同时做多件事情。

应用场景:只要你想让多个事件同时运行就需要用到多线程。

并发:在同一时刻,有多个指令在单个CPU上交替执行。

并行:在同一时刻,有多个指令在多个CPU上同时执行。

多线程的实现方式

1.继承Thread类的方式进行执行

步骤:

自己定义一个类继承Thread

重写run方法

创建子类的对象,并启动线程。

public class MyThread extends Thread{@Overridepublic void run() {for(int i=0;i<10;i++) {System.out.println("Hello");}}}
public class test {public static void main(String [] args)  {//创建子类对象,开启线程MyThread mt=new MyThread();mt.start();}
}

2.实现Runnable接口的方式进行实现

步骤:

自己定义一个类实现Runnable接口

重写里面的run方法

创建自己类的对象

创建一个Thread类的对象,并开启线程。

public class MyThread implements Runnable{@Overridepublic void run() {for(int i=0;i<10;i++) {System.out.println("world");}}}
public class test {public static void main(String [] args)  {//创建自己类的对象MyThread mt=new MyThread();//创建Thread对象,并开启线程Thread t=new Thread(mt);t.start();}
}

 

若想看看两个线程的相互执行,获取线程的名字:

public class MyThread implements Runnable{//在此获取线程的名字@Overridepublic void run() {for(int i=0;i<10;i++) {Thread t=Thread.currentThread();//获取当前线程对象System.out.println(t.getName()+":world");}}}
public class test {public static void main(String [] args)  {//创建自己类的对象MyThread mt=new MyThread();//创建Thread对象,并开启线程Thread t1=new Thread(mt);Thread t2=new Thread(mt);//给线程设置名字t1.setName("线程1");t2.setName("线程2");t1.start();t2.start();}
}

3.利用Callable接口和Future接口方式实现

特点:可以获取多线程运行的结果。

1)创建一个MyCallable类实现Callable接口;

2)重写call方法(是有返回值的,表示多线程运行的结果);

3)创建MyCallable的对象(表示多线程要执行的任务);

4)创建FutureTask的对象(作用管理多线程的运行结果);

5)创建Thread类的对象,并启动(表示线程)。

public class MyCallable implements Callable<Integer>{//计算1~10的和@Overridepublic Integer call() throws Exception {int sum=0;for(int i=0;i<=10;i++) {sum+=i;}return sum;}}
public class test {public static void main(String [] args) throws InterruptedException, ExecutionException  {MyCallable mc=new MyCallable();FutureTask<Integer> ft=new FutureTask<>(mc);//管理多线程运行结果Thread t=new Thread(ft);t.start();//开启线程//获取返回值Integer sum=ft.get();System.out.println(sum);}
}

多线程的成员方法

获取与设置线程的名字

注:若我们未给线程设置名字,线程也有默认的名字,格式:Thread-X(X为序号,从0开始)。

若我们要给线程设置名字,可以用set方法,也可以用构造方法(调用父类的构造方法)设置。

1)用set进行设置。

public class MyThread extends Thread{@Overridepublic void run() {for(int i=0;i<10;i++) {//获取线程名字System.out.println(getName()+":"+i);}}}public class test {public static void main(String [] args)  {MyThread t1=new MyThread();MyThread t2=new MyThread();//设置线程名字,用set方法设置t1.setName("线程1");t2.setName("线程2");t1.start();t2.start();}
}

线程之间交替运行。 

2)用构造方法进行设置

public class MyThread extends Thread{public MyThread() {//调用父类的构造方法}public MyThread(String name) {super(name);}@Overridepublic void run() {for(int i=0;i<10;i++) {System.out.println(getName()+":"+i);}}}
public class test {public static void main(String [] args)  {MyThread t1=new MyThread("飞机");MyThread t2=new MyThread("鼠标");t1.start();t2.start();}
}

线程休眠

static void sleep(long time)——让线程休眠指定时间,单位毫秒。

注:哪条线程执行到这个方法,那么哪条线程就会在这停留对应的时间。当时间到了之后,线程会自动醒来,执行下面的代码。

public class MyThread extends Thread{public MyThread() {//调用父类的构造方法}public MyThread(String name) {super(name);}@Overridepublic void run() {for(int i=0;i<10;i++) {try {Thread.sleep(1000);//每个一秒打印一个数据} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(getName()+":"+i);}}}
public class test {public static void main(String [] args)  {MyThread t1=new MyThread("飞机");MyThread t2=new MyThread("鼠标");t1.start();t2.start();}
}

线程的优先级

在java中使用抢占式调度,其特点为随机性。优先级为1~10,默认优先级为5,优先级越高,抢到CPU的概率越大,但并不是百分之一百抢到CPU。

public class MyThread extends Thread{@Overridepublic void run() {for(int i=0;i<7;i++) {System.out.println(getName()+":"+i);}}
}
public class test {public static void main(String [] args)  {MyThread t1=new MyThread();MyThread t2=new MyThread();//设置线程名字t1.setName("键盘");t2.setName("鼠标");//设置线程的优先级t1.setPriority(1);t2.setPriority(10);t1.start();t2.start();}
}

鼠标优先级大,鼠标先执行完毕。

守护线程

当其他非守护线程结束后,守护线程也会陆续结束(一般不会执行完毕)。

public class MyThread1 extends Thread{@Overridepublic void run() {for(int i=0;i<8;i++) {System.out.println(getName()+":"+i);}}
}
public class MyThread2 extends Thread{@Overridepublic void run() {for(int i=0;i<100;i++) {System.out.println(getName()+":"+i);}}
}
public class test {public static void main(String [] args)  {MyThread1 t1=new MyThread1();MyThread2 t2=new MyThread2();//设置线程名字t1.setName("线程");t2.setName("备份");//将t2设置为守护线程t2.setDaemon(true);t1.start();t2.start();}
}

礼让线程

礼让线程也叫出让线程,作用:尽可能使结果均匀一点。

public class MyThread extends Thread{@Overridepublic void run() {for(int i=0;i<8;i++) {System.out.println(getName()+":"+i);Thread.yield();//出让线程}}
}
public class test {public static void main(String [] args)  {MyThread t1=new MyThread();MyThread t2=new MyThread();//设置线程名字t1.setName("线程1");t2.setName("线程2");t1.start();t2.start();}
}

插入线程

public class MyThread extends Thread{@Overridepublic void run() {for(int i=0;i<100;i++) {System.out.println(getName()+":"+i);}}
}
public class test {public static void main(String [] args) throws InterruptedException  {MyThread t1=new MyThread();	t1.setName("土豆");t1.start();t1.join();//插入线程,将土豆线程插入到main线程之前for(int i=0;i<10;i++) {System.out.println("main线程"+i);}}
}

线程的生命周期

同步代码块

把操作共享的代码锁起来。

锁对象一定要求是唯一的。

 利用同步代码块可解决线程安全问题。若没有锁,可能会出现数据重复,数据超出要求的范围等等问题。

练习

某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票。

public class MyThread extends Thread{//设置一个静态数据,表示这个类所有对象可以共享static int ticket=0;//票数static Object obj=new Object();//锁对象一定要是唯一的@Overridepublic void run() {while(true) {synchronized(obj) {if(ticket<100) {try {Thread.sleep(100);//数据慢慢的输出} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}ticket++;System.out.println(getName()+"今天卖出第"+ticket+"票");}else {break;}}}}
}
public class test {public static void main(String [] args)  {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();}
}

同步方法

就是把synchronized关键字加到方法上。

同步方法是用同步代码块提取方法,改编而来的。

练习

某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票。(使用同步方法完成,技巧:先构建好同步代码块,在进行变换)

public class MyThread implements Runnable{//使用第二种方法int ticket=0;//使用第二种方法创建对象,不用设置这个为静态的@Overridepublic void run() {while(true) {       if (mehod()) break;         }}private synchronized boolean mehod() {//同步方法if(ticket==100) {return true;}else {try {Thread.sleep(100);//若想要看到多个线程交织进行,可采用sleep让线程睡一会} catch (final InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}ticket++;System.out.println(Thread.currentThread().getName()+"今天卖了第"+ticket+"张票");}return false;}}
public class test1 {public static void main(String[] args) {MyThread mt=new MyThread();Thread t1=new Thread(mt);Thread t2=new Thread(mt);Thread t3=new Thread(mt);t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();}
}

Lock锁


 练习

某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票。(利用Lock锁进行)

在Lock锁当中利用try-catch-finall来进行开锁关锁。,将while循环中的主体程序写完之后,从获得锁开始后利用快捷方法生成try-catch-finall,finally中写释放锁。

public class MyThread extends Thread{//设置一个静态数据,表示这个类所有对象可以共享static int ticket=0;Lock lock=new ReentrantLock();//创建一个锁对象@Overridepublic void run() {while(true) {//  synchronized (MyThread.class) {lock.lock();//获得锁try {if (ticket < 100) {Thread.sleep(100);//数据慢慢的输出ticket++;System.out.println(getName() + "今天卖出第" + ticket + "票");} else {break;}} catch (InterruptedException e) {throw new RuntimeException(e);} finally {lock.unlock();//释放锁}// }}}
}
public class test1 {public static void main(String[] args) {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();}}

书写进程步骤:

生产者和消费者(等待唤醒机制)

生产者消费者模式是一个十分经典的多线程协作的模式。

例如:吃货和厨师(消费者和生产者)

常见方法:

实现方法1:基本方法 

 有:生产者、消费者、控制生产者和消费者的代码

先写中间:

public class Desk {//中间变量//作用:控制消费者和生产者的代码//定义桌子状态,0无面条,1有面条public static int state=0;//定义吃货最大能吃多少,总个数public static int count=10;//定义锁对象public static Object lock=new Object();
}

消费者:

public class Eat extends Thread{//消费者//循环//同步代码块(同步方法,锁方法均可)//判断共享数据是否到末尾(先写到了末尾,再写未到末尾)@Overridepublic void run(){while(true){synchronized (Desk.lock){//锁对象//判断共享数据是否到末尾if(Desk.count==0){break;}else{//先判断桌子上物品状态if(Desk.state==0){//如果没有消费者等待try {Desk.lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}else{//如果有Desk.count--;System.out.println("这个人还能再吃"+Desk.count+"碗饭");//吃完唤醒厨师继续做Desk.lock.notifyAll();//修改桌子状态Desk.state=0;}}}}}
}

生产者:

public class Cook extends Thread{//生产者@Overridepublic void run(){while(true){synchronized (Desk.lock){if(Desk.count==0){break;}else{//判断桌子状态if(Desk.state==1){//如果有,就等待try {Desk.lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}else{//如果没有System.out.println("厨师做了一碗面");//修改桌子状态Desk.state=1;//唤醒消费者Desk.lock.notifyAll();}}}}}
}

测试类:

public class test1 {public static void main(String[] args) {Cook c=new Cook();Eat e=new Eat();//设置名字c.setName("厨师:");e.setName("吃货");//开启线程c.start();e.start();}}

实现方法2:阻塞队列方法实现

 阻塞队列的接口和实现类:

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) {throw new RuntimeException(e);}}}
}
public class Eat extends Thread{//消费者ArrayBlockingQueue<String> queue;public Eat(ArrayBlockingQueue<String> queue) {this.queue=queue;}@Overridepublic void run(){while(true){try {String food=queue.take();System.out.println(food);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
public class test1 {public static void main(String[] args) {//创建阻塞队列ArrayBlockingQueue<String> queue=new ArrayBlockingQueue<>(10);//定义阻塞队列中的队列长度Cook c=new Cook(queue);Eat e=new Eat(queue);c.setName("厨师");e.setName("吃货");c.start();e.start();}}

线程的六种状态

综合练习

练习1:抢红包

(因为获取Double类型的随机数只能在JDK17中,抢红包实际抢到的是小数,在此题下理想化,认为是整数)
抢红包也用到了多线程。假设: 100块,分成了3个包,现在有5个人去抢。其中,红包是共享数据。5个人是5条线程。
打印结果如下:
XXX抢到了XXX元
XXX抢到了XXX元
XXX抢到了XXX元
XXX没抢到
XXX没抢到

package test1;
import java.util.Random;
public class MyThread extends  Thread{//定义静态变量static  int money=100;//总金额static int count=3;//红包个数static final int MIN=1;//抢到的金额的最小值@Overridepublic void run() {synchronized (MyThread.class) {//判断金额if (money == 0) {System.out.println(getName() + "没有抢到红包");} else {//判断红包个数int prize = 0;//中奖金额if (count == 1) {//只剩一个红包,中奖金额就是余额prize = money;} else {//进行随机数,获取红包金额Random r = new Random();int bound = money - (count - 1) * MIN;prize = r.nextInt(bound);if (prize < MIN) {prize = MIN;}}money = money - prize;count--;System.out.println(getName() + "抢到了" + prize + "元");}}}
}
public class test1 {public static void main(String[] args) {MyThread t1=new MyThread();MyThread t2=new MyThread();MyThread t3=new MyThread();MyThread t4=new MyThread();MyThread t5=new MyThread();t1.setName("张三");t2.setName("李四");t3.setName("王五");t4.setName("赵六");t5.setName("导航");t1.start();t2.start();t3.start();t4.start();t5.start();}}

练习2:抽奖池抽奖

有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为{10,5,20,50,100,200,500,800,2,80,300,700};
创建两个抽奖箱(线程)设置线程名称分别为“ 抽奖箱1”,‘ '抽奖箱2”随机从抽奖池中获取奖项元素并打印在控制台上格式如下:
每次抽出一个奖项就打印一个(随机)
抽奖箱1又产生了一个10元大奖
抽奖箱1又产生了一个100元大奖
抽奖箱2又产生了一个700元大奖
分析:可用集合和数组进行,但数组去重比较复杂,利用集合完成。

public class MyThread extends  Thread {ArrayList<Integer> list;//静态变量//可以利用构造方法public MyThread(ArrayList<Integer> list) {this.list = list;}@Overridepublic void run() {while (true) {synchronized (MyThread.class) {if (list.size() == 0) {break;} else {Collections.shuffle(list);//打乱list数据int prize = list.remove(0);System.out.println(getName() + "又产生了一个" + prize + "大奖");}}try {Thread.sleep(100);//目的是可以让两个线程交替进行显示} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
public class test1 {public static void main(String[] args) {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();}}

练习3:多线程统计并求最大值

在上一题基础上继续完成如下需求:
每次抽的过程中,不打印,抽完时一次性打印(随机),在此次抽奖过程中,抽奖箱1总共产生了6个奖项。
分别为: 10,20,100,500,2,300最高奖项为300元,总计额为932元在此次抽奖过程中,抽奖箱2总共产生了6个奖项。
分别为: 5, 50,200,800,80,700最高奖项为800元,总计额为1835元

解法一:对第一个程序进行改编
 

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);int max=0;int count=0;for(int i=0;i<list1.size();i++){if(max<= list1.get(i)){max=list1.get(i);}count+=list1.get(i);}System.out.println("抽奖箱1最大奖"+max);System.out.println("抽奖箱1总计"+count);}else if("抽奖箱2".equals(getName())) {System.out.println("抽奖箱2"+list2);int max=0;int count=0;for(int i=0;i<list2.size();i++){if(max<= list2.get(i)){max=list2.get(i);}count+=list2.get(i);}System.out.println("抽奖箱2最大奖"+max);System.out.println("抽奖箱2总计"+count);}break;}else{Collections.shuffle(list);//打乱list数据int prize=list.remove(0);//进行判断if("抽奖箱1".equals(getName())){list1.add(prize);}else if("抽奖箱2".equals(getName())){list2.add(prize);}}}
}try {Thread.sleep(10);//目的是可以让两个线程交替进行显示} catch (InterruptedException e) {throw new RuntimeException(e);}}}

解法二:线程栈

public class MyThread extends  Thread {ArrayList<Integer> list;//静态变量//可以利用构造方法public MyThread(ArrayList<Integer> list) {this.list = list;}@Overridepublic void run() {ArrayList<Integer> List=new ArrayList<>();while (true) {synchronized (MyThread.class) {if (list.size() == 0) {System.out.println(getName()+List);break;} else {Collections.shuffle(list);//打乱list数据int prize = list.remove(0);List.add(prize);}}try {Thread.sleep(100);//目的是可以让两个线程交替进行显示} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

练习4:多线程之间的比较

在上一题基础上继续完成如下需求:在此次抽奖过程中,抽奖箱1总共产生了6个奖项,分别为: 10,20,100,500,2,300,最高奖项为300元,总计额为932元
在此次抽奖过程中,抽奖箱2总共产生了6个奖项,分别为: 5,50,200,800,80,700
最高奖项为800元,总计额为1835元
在此次抽奖过程中,抽奖箱2中产生了最大奖项,该奖项金额为800元
分析:最大奖项应该这两个线程结束后再比较,采用有返回值的线程创建方法,将最大值返回后打印。

public class MyCallable implements Callable<Integer> {ArrayList<Integer> list;//静态变量//可以利用构造方法public MyCallable(ArrayList<Integer> list) {this.list = list;}@Overridepublic Integer call() throws Exception {ArrayList<Integer> List=new ArrayList<>();while (true) {synchronized (MyCallable.class) {if (list.size() == 0) {System.out.println(Thread.currentThread().getName()+List);break;} else {Collections.shuffle(list);//打乱list数据int prize = list.remove(0);List.add(prize);}}Thread.sleep(100);//目的是可以让两个线程交替进行显示}//把集合中的最大值返回if(List.size()==0){return null;}else{return Collections.max(List);}}
}
public class test1 {public static void main(String[] args) throws ExecutionException, InterruptedException {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();int max1=ft1.get();int max2=ft2.get();System.out.println(max1);System.out.println(max2);}}

线程池

核心原理:

创建线程池

创建一个没有上限的线程池:

public class MyRunnable implements Runnable{@Overridepublic void run() {for(int i=1;i<=100;i++){System.out.println(Thread.currentThread().getName()+"--"+i);}}
}
public class test1 {public static void main(String[] args)  {//创建线程池对象ExecutorService poll= Executors.newCachedThreadPool();//提交任务poll.submit(new MyRunnable());poll.submit(new MyRunnable());poll.submit(new MyRunnable());//线程池一般不会关闭}}

 

创建一个有上限的线程池:

public class test1 {public static void main(String[] args)  {//创建线程池对象ExecutorService poll= Executors.newFixedThreadPool(2);//提交任务poll.submit(new MyRunnable());poll.submit(new MyRunnable());poll.submit(new MyRunnable());//线程池一般不会关闭}}

自定义线程池

系统先会创建服务于任务1、2、3的线程1、2、3;再将任务4、5、6拿去排队; 再调用临时线程服务于任务7、8、9;任务10会触发任务拒绝策略。

任务拒绝策略

创建自定义线程池:

public class test1 {public static void main(String[] args)  {//自定义线程池ThreadPoolExecutor pool=new ThreadPoolExecutor(3,//核心线程的数量6,//最大线程数60,//空闲线程最大存活时间TimeUnit.SECONDS,//时间单位new ArrayBlockingQueue<>(3),//任务队列,相当于阻塞队列Executors.defaultThreadFactory(),//创建线程工厂:线程池如何获取到一个线程new ThreadPoolExecutor.AbortPolicy()//任务拒绝策略);}}

线程池多大合适

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/23815.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【机器学习300问】108、什么是多项式回归模型?

一、多项式回归是什么 &#xff08;1&#xff09;举例说明 假设你经营着一家农场&#xff0c;想要根据土地面积来预测作物的产量。如果你只用线性模型&#xff08;即&#xff09;&#xff0c;你可能会发现它并不足以描述实际的产量情况&#xff0c;因为实际产量可能会随着土地…

Acwing 786.第K个数

Acwing 786.第K个数 题目描述 786. 第k个数 - AcWing题库 运行代码 #include <iostream> #include <algorithm> using namespace std; const int N 100010; int q[N];int main() {int n, k;scanf("%d%d", &n, &k);for (int i 0; i < n; …

opencv进阶 ——(十三)基于三角剖分实现换脸

换脸的关键在于人脸对齐&#xff0c;人脸对齐主要包括以下几点&#xff1a; 1、人脸可能存在一定的角度&#xff0c;因此需要先将倾斜方向进行对齐 2、大小对齐&#xff0c;将模板人脸的大小缩放到同一大小 3、要想有好的效果&#xff0c;关键点选取很重要 4、人脸对齐后&a…

黑马python-JavaScript

1.JavaScript的定义&#xff1a; JavaScript是运行在浏览器端的脚步语言&#xff0c;是由浏览器解释执行的、简称js。它能够让网页和用户有交互功能&#xff0c;增加良好的用户体验效果 2.使用方式&#xff1a; 1.行内式&#xff08;主要用于事件&#xff09; <input type&q…

【大数据】计算引擎:Spark核心概念

目录 前言 1.什么是Spark 2.核心概念 2.1.Spark如何拉高计算性能 2.2.RDD 2.3.Stage 3.运行流程 前言 本文是作者大数据系列中的一文&#xff0c;专栏地址&#xff1a; https://blog.csdn.net/joker_zjn/category_12631789.html?spm1001.2014.3001.5482 该系列会成体…

JAVA技术设计模式

设计模式结构图 设计原则 职责单一原则接口隔离原则 一个类对另一个类的依赖应该建立在最小的接口上 依赖倒置面向接口编程,参数或变量,依赖注入,使用父类 开闭原则 对扩展开放(对提供方),对修改关闭(对使用方) 用抽象构建框架,用实现扩展细节 里氏替换原则…

java中的双列集合(Map,HashMap,TreeMap,LinkedHashMap)

双列集合的特点 双列集合一次需要存一对数据&#xff0c;分别为键和值 键不能重复&#xff0c;值可以重复 键和值是一一对应的&#xff0c;每一个键只能找到自己对应的值 键值这个整体 &#xff0c;我们称之为“键值对”或者“键值对对象”&#xff0c;在Java中叫做“Entry对象…

CAPL如何发送一条UDP报文

UDP作为传输层协议,本身并不具有可靠性传输特点,所以不需要建立连接通道,可以直接发送数据。当然,前提是需要知道对方的通信端点,也就是IP地址和端口号。 端口号是传输层协议中最显著的特征,传输层根据它来确定上层绑定的应用程序,以达到把数据交给上层应用处理的目的。…

【Pytorch】计算机视觉项目——卷积神经网络TinyVGG模型图像分类(模型预测)

介绍 这篇文章是《【Pytorch】计算机视觉项目——卷积神经网络TinyVGG模型图像分类&#xff08;如何使用自定义数据集&#xff09;》的最后一部分内容&#xff1a;模型预测。 在本文中&#xff0c;我们将介绍如何测试模型的预测效果——让已训练好模型对一张新的图片进行分类&a…

在 SEO 中,一个好的网页必须具备哪些 HTML 标签和属性?

搜索引擎优化 &#xff08;SEO&#xff09; 是涉及提高网站在搜索引擎上的可见性的过程。这是通过提高网站在搜索引擎结果页面&#xff08;例如Google&#xff09;上的排名来实现的。网站在这些页面上的显示位置越高&#xff0c;就越有可能获得更大的流量。 搜索引擎优化涉及了…

跑mask2former(自用)

1. 运行docker 基本命令&#xff1a; sudo docker ps -a &#xff08;列出所有容器状态&#xff09; sudo docker run -dit -v /hdd/lyh/mask2former:/mask --gpus "device0,1" --shm-size 16G --name mask 11.1:v6 &#xff08;创建docker容器&…

Mac系统使用COLMAP

安装教程 如有出入&#xff0c;参照官网手册最新版 Installation — COLMAP 3.9-dev documentation 首先确保mac上安装了Homebrew 1.安装依赖项 brew install \cmake \ninja \boost \eigen \flann \freeimage \metis \glog \googletest \ceres-solver \qt5 \glew \cgal \s…

万里长城第一步——尚庭公寓【技术概述】

简略版&#xff1a; 项目概述主要是移动端&#xff08;房源检索&#xff1b;预约看房&#xff0c;租赁管理&#xff0c;浏览历史&#xff09;和后台管理&#xff08;管理员对房源进行操作&#xff09;&#xff1b; 项目使用前后端分离的方法&#xff0c;主要以后端为主&#xf…

rpm安装

rpm安装 命令格式&#xff1a; rpm 【选项】 文件名 选项&#xff1a; -i&#xff1a;安装软件 -v:显示安装过程信息 -h:用#表示安装进度&#xff0c;一个#代表2% -ivh&#xff1a;安装软件&#xff0c;显示安装过程 -e:卸载软件 -q:查看软件是否安装 -ql&#xff1…

信息系统项目管理师0147:工具与技术(9项目范围管理—9.3规划范围管理—9.3.2工具与技术)

点击查看专栏目录 文章目录 9.3.2 工具与技术 9.3.2 工具与技术 专家判断 规划范围管理过程中&#xff0c;应征求具备如下领域相关专业知识或接受过相关培训的个人或小组 的意见&#xff0c;涉及的领域包括&#xff1a;以往类似项目&#xff1b;特定行业、学科和应用领域的信息…

学习anjuke的过程

一、抓包 先看看12.25.1版本的APP是不是还能使用&#xff0c;如果还能使用我们就先破解低版本的。打开APP后发现还能正常使用&#xff0c;因为低版本的难度低我们就破解这个版本。低版本和高版本的算法是一样的&#xff0c;算法破解之后我们后续抓包替换接口就行了。手机安装上…

SQLAlchemy 模型中数据的错误表示

1. 问题背景 在使用 SQLAlchemy 0.6.0 版本&#xff08;也曾尝试使用 0.6.4 版本&#xff09;的 Pylons 应用程序中遇到了一个 SQLAlchemy ORM 问题。该问题出现在使用 psycopg2 作为数据库驱动程序、连接至 Postgresql 8.2 数据库的环境中。定义了一个 User 模型对象&#xf…

FreeRTOS基础(十一):消息队列

本文将详细全方位的讲解FreeRTOS的消息队列&#xff0c;其实在FreeRTOS中消息队列的重要性也不言而喻&#xff0c;与FreeRTOS任务调度同等重要&#xff0c;因为后面的各种信号量基本都是基于消息队列的。 目录 一、消息队列的简介 1.1 产生的原因 1.2 消息队列的解决办法 …

【数据库】SQL零基础入门学习

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

重邮计算机网络803-(2)物理层

一.物理层 1.介绍 物理层的主要任务描述为确定与传输媒体的接口的一些特性&#xff0c;即&#xff1a; ①机械特性 指明接口所用接线器的形状和尺寸、引线数目和排列、固定和锁定装置等等。 ②电气特性 指明在接口电缆的各条线上出现的电压的范围。 ③功能特性 指明某条线上…