【多线程】线程池Future和FutureTask

【多线程】线程池Future和FutureTask

  • 【一】Future概述
    • 【1】Future的出现原因
    • 【2】Future结构图
  • 【二】Future详解
    • 【1】Future接口源码
    • 【2】Future的5个方法
    • 【3】ThreadPoolExecutor提供了三个方法,来获取返回值
      • (1)submit(Runnable r)
      • (2)submit(Runnable r,T result)
      • (3)submit(Callable c) 【推荐】
    • 【4】Future使用案例
      • (1)Future的案例一
      • (2)FutureTask的案例二
      • (3)FutureTask的案例三
      • (4)FutureTask的案例四
    • 【5】总结
  • 【三】其他案例
    • 【1】FutureTask获取分布式锁
    • 【2】FutureTask监控任务的处理时长是否超时
    • 【3】FutureTask监控任务的处理时长是否超时(2)

【一】Future概述

【1】Future的出现原因

我们在Java线程池ThreadPoolExecutor使用详解中利用execute(Runnable r)方法来异步执行任务,但是有一个缺点,就是无法执行带有返回值的任务。

【2】Future结构图

在这里插入图片描述
FutureTask实现了RunnableFuture接口,而RunnableFuture继承了Runnable和Future,也就是说FutureTask既是Runnable,也是Future。

【二】Future详解

Future表示一个任务的周期,并提供了相应的方法来判断是否已经完成或者取消,以及获取任务的结果和取消任务。

(1)Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果

(2)Future是一个接口,是无法生成一个实例的,所以又有了FutureTask。FutureTask实现了RunnableFuture接口,RunnableFuture接口又实现了Runnable接口和Future接口。所以FutureTask既可以被当做Runnable来执行,也可以被当做Future来获取Callable的返回结果。

【1】Future接口源码

public interface Future<V> {boolean cancel(boolean mayInterruptIfRunning);boolean isCancelled();boolean isDone();V get() throws InterruptedException, ExecutionException;V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;
}

【2】Future的5个方法

1、cancel():取消一个任务,并返回取消结果。参数表示是否中断线程
2、isCancelled():任务是否取消成功
3、isDone():判断当前任务是否执行完毕,包括正常执行完毕、执行异常或者任务取消
4、get():获取任务执行结果,任务结束之前会阻塞
5、get(long count,TimeUnit):在指定时间内尝试获取执行结果。若超时则抛出超时异常

在这里插入图片描述

【3】ThreadPoolExecutor提供了三个方法,来获取返回值

在这里插入图片描述

(1)submit(Runnable r)

因为Runnable的run方法是void的,没有返回值,所以Future.get()是null。

源码

static final class RunnableAdapter<T> implements Callable<T> {final Runnable task;final T result;RunnableAdapter(Runnable task, T result) {this.task = task;this.result = result;}public T call() {task.run();return result;}
}

将runnable封装为一个自定义的Callable对象,result为null,具体的流程可以看最底下的源码。

(2)submit(Runnable r,T result)

T result就是传入的对象。我们可以通过自定义Runnable来将result传入到自定义的Runnable中,在run()方法中对结果写入到result中。其中返回的对象future.get()==result。

案例

public class TestThreadExecutor {public static void main(String[] args) throws ExecutionException, InterruptedException {BlockingQueue queue = new LinkedBlockingQueue(10);ThreadPoolExecutor executor = new ThreadPoolExecutor(4,10,2,TimeUnit.SECONDS,queue);//设置coreThread如果超时也会被取消executor.allowCoreThreadTimeOut(true);int[] arr = new int[2];MyRunnable runnable = new MyRunnable(arr);Future f = executor.submit(runnable,arr);System.out.println(Arrays.toString((int[])f.get()));}
}
-------------------------------------------------------------------------
class MyRunnable implements Runnable {private int[] arr;public MyRunnable(int[] arr){this.arr = arr;}@Overridepublic void run() {arr[0] = 111;}
}

在这里插入图片描述
源码

static final class RunnableAdapter<T> implements Callable<T> {final Runnable task;final T result;RunnableAdapter(Runnable task, T result) {this.task = task;this.result = result;}public T call() {task.run();return result;}
}

将runnable封装为一个自定义的Callable对象,result为传入的对象,具体的流程可以看最底下的源码。

(3)submit(Callable c) 【推荐】

submit(Runnable r,T result)不太好用,需要每次都自定义Runnable和result,太麻烦了。

Callable更加好用,传入一个Callable对象就行。
Callable和Runnable是大致一样,只不过Callable能够返回值。

Callable callable = new Callable() {@Overridepublic Object call() throws Exception {String name = "777";name += "6666";return name;}
};Future f = executor.submit(callable);
System.out.println(f.get());

在这里插入图片描述
从上述代码可以看出,Callable中call()返回的对象就是future.get()得到的对象。

源码

public <T> Future<T> submit(Callable<T> task) {if (task == null) throw new NullPointerException();RunnableFuture<T> ftask = newTaskFor(task);//利用callable新建了一个FutureTaskexecute(ftask);//会调用ftask.run()方法return ftask;//返回一个FutureTask对象
}protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {return new FutureTask<T>(callable);
}public FutureTask(Callable<V> callable) {if (callable == null)throw new NullPointerException();this.callable = callable;this.state = NEW;  
}public void run() {if (state != NEW ||!UNSAFE.compareAndSwapObject(this, runnerOffset,null, Thread.currentThread()))return;try {Callable<V> c = callable;if (c != null && state == NEW) {V result;boolean ran;try {//执行callable.call()result = c.call();ran = true;} catch (Throwable ex) {result = null;ran = false;setException(ex);}if (ran)//将outcome设置为resultset(result);}} finally {runner = null;int s = state;if (s >= INTERRUPTING)handlePossibleCancellationInterrupt(s);}
}protected void set(V v) {if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {//这里这里这里outcome = v;UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final statefinishCompletion();}
}

future.get()

public V get() throws InterruptedException, ExecutionException {int s = state;if (s <= COMPLETING)//阻塞s = awaitDone(false, 0L);//这里这里这里return report(s);
}private V report(int s) throws ExecutionException {//这里这里这里Object x = outcome;if (s == NORMAL)//这里这里这里return (V)x;if (s >= CANCELLED)throw new CancellationException();throw new ExecutionException((Throwable)x);
}

【4】Future使用案例

(1)Future的案例一

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;public class Main {public static void main(String[] args) {ExecutorService executorService = Executors.newCachedThreadPool();List<Future<String>> resultList = new ArrayList<Future<String>>();// 创建10个任务并执行for (int i = 0; i < 10; i++) {// 使用ExecutorService执行Callable类型的任务,并将结果保存在future变量中Future<String> future = executorService.submit(new TaskWithResult(i));// 将任务执行结果存储到List中resultList.add(future);}executorService.shutdown();// 遍历任务的结果for (Future<String> fs : resultList) {try {System.out.println(fs.get()); // 打印各个线程(任务)执行的结果} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {executorService.shutdownNow();e.printStackTrace();return;}}}
}
class TaskWithResult implements Callable<String> {private int id;public TaskWithResult(int id) {this.id = id;}/*** 任务的具体过程,一旦任务传给ExecutorService的submit方法,则该方法自动在一个线程上执行。** @return* @throws Exception*/public String call() throws Exception {System.out.println("call()方法被自动调用,干活!!!             " + Thread.currentThread().getName());if (new Random().nextBoolean())throw new TaskException("Meet error in task." + Thread.currentThread().getName());// 一个模拟耗时的操作for (int i = 999999999; i > 0; i--);return "call()方法被自动调用,任务的结果是:" + id + "    " + Thread.currentThread().getName();}
}class TaskException extends Exception {public TaskException(String message) {super(message);}
}

执行结果:

call()方法被自动调用,干活!!! pool-1-thread-2
call()方法被自动调用,干活!!! pool-1-thread-6
call()方法被自动调用,干活!!! pool-1-thread-1
call()方法被自动调用,任务的结果是:0 pool-1-thread-1
call()方法被自动调用,任务的结果是:1 pool-1-thread-2
call()方法被自动调用,干活!!! pool-1-thread-4
call()方法被自动调用,干活!!! pool-1-thread-9
call()方法被自动调用,干活!!! pool-1-thread-8
call()方法被自动调用,干活!!! pool-1-thread-10
call()方法被自动调用,干活!!! pool-1-thread-5
call()方法被自动调用,干活!!! pool-1-thread-3
call()方法被自动调用,干活!!! pool-1-thread-7
call()方法被自动调用,任务的结果是:2 pool-1-thread-3
call()方法被自动调用,任务的结果是:3 pool-1-thread-4
java.util.concurrent.ExecutionException: com.company.TaskException: Meet error in task.pool-1-thread-5
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at com.company.Main.main(Main.java:29)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: com.company.TaskException: Meet error in task.pool-1-thread-5
at com.company.TaskWithResult.call(Main.java:56)
at com.company.TaskWithResult.call(Main.java:40)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)

(2)FutureTask的案例二

FutureTask是一个具体的实现类,ThreadPoolExecutor的submit方法返回的就是一个Future的实现,这个实现就是FutureTask的一个具体实例,FutureTask帮助实现了具体的任务执行,以及和Future接口中的get方法的关联。

import java.util.Date;
import java.text.SimpleDateFormat;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class Main {public static void main(String[] args) {SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 初始化一个Callable对象和FutureTask对象Callable pAccount = new PrivateAccount();FutureTask futureTask = new FutureTask(pAccount);// 使用futureTask创建一个线程Thread pAccountThread = new Thread(futureTask);System.out.println("futureTask线程现在开始启动,启动时间为:" + df.format(new Date()));pAccountThread.start();System.out.println("futureTask线程开始执行其他任务");// 从其他账户获取总金额int totalMoney = new Random().nextInt(100000);System.out.println("现在你在其他账户中的总金额为" + totalMoney);System.out.println("等待私有账户总金额统计完毕...");// 测试后台的计算线程是否完成,如果未完成则等待while (!futureTask.isDone()) {try {Thread.sleep(500);System.out.println("私有账户计算未完成继续等待...");} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("futureTask线程计算完毕,此时时间为" + df.format(new Date()));Integer privateAccountMoney = null;try {privateAccountMoney = (Integer) futureTask.get();} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}System.out.println("您现在的总金额为:" + totalMoney +"||"+privateAccountMoney.intValue());}
}class PrivateAccount implements Callable {Integer totalMoney;@Overridepublic Object call() throws Exception {Thread.sleep(5000);totalMoney = new Integer(new Random().nextInt(10000));System.out.println("您当前有" + totalMoney + "在您的私有账户中");return totalMoney;}
}

执行结果

futureTask线程现在开始启动,启动时间为:2016-11-30 16:34:57
futureTask线程开始执行其他任务
现在你在其他账户中的总金额为17113
等待私有账户总金额统计完毕…
私有账户计算未完成继续等待…
私有账户计算未完成继续等待…
私有账户计算未完成继续等待…
私有账户计算未完成继续等待…
私有账户计算未完成继续等待…
私有账户计算未完成继续等待…
私有账户计算未完成继续等待…
私有账户计算未完成继续等待…
私有账户计算未完成继续等待…
您当前有8776在您的私有账户中
私有账户计算未完成继续等待…
futureTask线程计算完毕,此时时间为2016-11-30 16:35:02
您现在的总金额为:17113||8776

(3)FutureTask的案例三

import java.util.Date;
import java.text.SimpleDateFormat;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class Main {public static void main(String[] args) {SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");FutureTask<String> futureTask=new FutureTask<>(new Callable<String>() {//@Overridepublic String call() throws Exception {// TODO Auto-generated method stubreturn "回调完成";}});Thread thread = new Thread(futureTask);System.out.println("启动时间为:" + df.format(new Date()));thread.start();try {String str=futureTask.get();if(str.equals("回调完成"))System.out.println("异步任务完成!");elseSystem.out.println("Completed!");} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (ExecutionException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}

执行结果

启动时间为:2016-12-01 09:37:03
异步任务完成!

(4)FutureTask的案例四

public static void main(String[] args) {ExecutorService service = Executors.newCachedThreadPool();Task task = new Task();FutureTask<Integer> futureTask = new FutureTask<>(task);service.submit(futureTask);service.shutdown();System.out.println("主线程正在执行任务。。。");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}try {//阻塞直至任务完成System.out.println("-------------------------------------");System.out.println("执行结果为:" + futureTask.get());} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}System.out.println("任务执行完成。。。");}static class Task implements Callable<Integer> {@Overridepublic Integer call() throws Exception {try {System.out.println("从线程正在执行任务。。。");Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}int num = 0;for (int i = 0; i <= 100; i++) {num += i;}return num;}}

futureTask.get()执行时如果该任务已经执行完了则直接返回执行结果,如果没有执行完则线程会阻塞在这里,直至任务执行完毕。还可以用get(long timeout, TimeUnit unit)来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。

【5】总结

总的来说Future模式的思想就是在子线程进行执行的时候,主线程不阻塞继续执行。等到主线程需要子线程的结果的时候再去获取子线程的结果(此时子线程没有执行完成的话就会阻塞直至执行完成)。主线程还可以根据一定的业务逻辑去判断是否要取消执行子线程,还可以设置一个超时时间。若阻塞时间超过了超时时间子线程仍然没有执行完成的话,可以直接返回null。

【三】其他案例

【1】FutureTask获取分布式锁

String taskInstanceKey = getTaskInstanceKey(beSubmitedTask);
//对实例加锁
RLock instanceLock = redissonClient.getLock(taskInstanceKey);
FutureTask<Boolean> futureTask = new FutureTask<>(() -> instanceLock.tryLock());
new Thread(futureTask).start();
if (!futureTask.get()) {log.info("指标固化获取锁失败{}", taskInstanceKey);return;
}

使用futureTask 取调用tryLock尝试获取锁,如果返回值为空,说明获取锁失败

【2】FutureTask监控任务的处理时长是否超时

这里采用FutureTask开启一个线程调用jdbc执行方法,主方法在10分钟后进行查询是否完成,根据isDone的结果去处理,简单介绍下FutureTask几个常用的方法:
(1)cancel方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。
(2)isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。
(3)isDone方法表示任务是否已经完成,若任务完成,则返回true;
(4)get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
(5)get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。
这里使用的是isDone去完成,当然也可以使用et(long timeout, TimeUnit unit)定时去获取返回结果
上代码

public static void readSqlThred(String sqlTest) {ExecutorService executor = Executors.newSingleThreadExecutor();FutureTask<String> future = new FutureTask<String>(new Callable<String>() {@Overridepublic String call() throws Exception {dealWithSql(sqlTest);//执行sqlreturn "执行完成";}});executor.execute(future);try {Thread.sleep(1000*6);if(future.isDone()) {System.out.println("sql执行完毕");}else {System.out.println("警告,sql未执行完毕,开始发送邮件..............");}}catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}
}

这里我是让线程睡了10分钟,10分钟后去取这个方法是否执行完成

【3】FutureTask监控任务的处理时长是否超时(2)

(1)程序中某方法挺耗时,但想超过XX数值则按失败算;
(2)程序中需要调用外部接口,要考虑外部接口不可用,或卡死问题。

针对以上问题,我们都想在程序中给某方法添加超时判断;运行程序等待该方法多长时间,超过则另作处理。


public static void main(String[] args){Thread t = null;try{FutureTask<Boolean> task = new FutureTask<Boolean>(new Callable<Boolean>() {public Boolean call() throws Exception {Thread.sleep(10*100);//处理方法return true;  //返回处理结果}});t = new Thread(task);t.start();boolean bl = task.get(10*10, TimeUnit.MILLISECONDS); //允许阻塞等待方法处理1秒,超出则抛异常System.out.println(bl);System.out.println(t.getState());}catch(Exception e1){e1.printStackTrace();System.out.println(t.getState());t.interrupt(); //在超时情况下上面新生成的线程不会关闭直到方法处理完毕,需要中断}}

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

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

相关文章

【ARM 裸机】汇编 led 驱动之原理分析

1、我们为什么要学习汇编&#xff1f;&#xff1f;&#xff1f; 之前我们或许接触过 STM32 以及其他的 32 位的 MCU ,都是基于 C 语言环境进行编程的&#xff0c;都没怎么注意汇编&#xff0c;是因为 ST 公司早已将启动文件写好了&#xff0c;新建一个 STM32 工程的时候&#…

回归预测 | Matlab实现SSA-GRNN麻雀算法优化广义回归神经网络多变量回归预测(含优化前后预测可视化)

回归预测 | Matlab实现SSA-GRNN麻雀算法优化广义回归神经网络多变量回归预测(含优化前后预测可视化) 目录 回归预测 | Matlab实现SSA-GRNN麻雀算法优化广义回归神经网络多变量回归预测(含优化前后预测可视化)预测效果基本介绍程序设计参考资料预测效果

图像生成:Pytorch实现一个简单的对抗生成网络模型

图像生成&#xff1a;Pytorch实现一个简单的对抗生成网络模型 前言相关介绍具体步骤准备并读取数据集定义生成器定义判别器定义损失函数定义优化器开始训练完整代码 训练生成的图片 前言 由于本人水平有限&#xff0c;难免出现错漏&#xff0c;敬请批评改正。更多精彩内容&…

实战Java高并发程序设计课

课程介绍 实战Java高并发程序设计课是一门针对Java开发者的培训课程&#xff0c;重点关注如何设计和优化高并发的程序。学员将学习到并发编程的基本概念、线程池的使用、锁机制、并发集合等技术&#xff0c;并通过实际案例进行实践操作。这门课程旨在帮助开发者掌握并发编程的…

最祥解决python 将Dataframe格式数据上传数据库所碰到的问题

碰到的问题 上传Datafrane格式的数据到数据库 会碰见很多错误 举几个很普遍遇到的问题(主要以SqlServer举例) 这里解释下 将截断字符串或二进制数据 这个是字符长度超过数据库设置的长度 然后还有字符转int失败 或者字符串转换日期/或时间失败 这个是碰到的需要解决的最多的问…

Java面试题戏剧

目录 第一幕 、第一场&#xff09;某大厦楼下大门前第二场&#xff09;电梯中第三场&#xff09;走廊中 第二幕、第一场&#xff09;公司前台第二场&#xff09;公司卫生间 第三幕、第一场&#xff09;一场异常面试 第四幕 、第一场&#xff09;大厦楼下门口第二场&#xff09;…

Lobe UI - 基于 AntDesign 开发的 AIGC Web 应用的开源 UI 组件库

今天推荐一个可以快速开发 ChatGPT UI 界面的组件库&#xff0c;质量很高&#xff0c;拿来就能用。 Lobe UI 是由 lobehub 团队开发的一套 web UI 组件库&#xff0c;和我之前推荐的很多通用型的 UI 组件库不同&#xff0c;Lobe UI 是专门为目前火热的 AIGC 应用开发而打造&am…

鸿蒙实战开发-如何实现选择并查看文档与媒体文件

介绍 应用使用ohos.file.picker、ohos.multimedia.mediaLibrary、ohos.file.fs 等接口&#xff0c;实现了picker拉起文档编辑保存、拉起系统相册图片查看、拉起视频并播放的功能。 效果预览 使用说明&#xff1a; 在首页&#xff0c;应用展示出最近打开过的文档信息&#xf…

马云最新发声:AI时代刚刚到来,一切才刚开始,我们正当其时!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

3. Spring 注解存储对象 Bean的命名规范

从Java5.0开始&#xff0c;Java开始支持注解。Spring做为Java生态中的领军框架&#xff0c;从2.5版本后也开始支持注解。相比起之前使用xml来配置Spring框架&#xff0c;使用注解提供了更多的控制Spring框架的方式。 SpringFramework版本对应jdk版本重要特性SpringFramework 1…

Unity之Unity面试题(五)

内容将会持续更新&#xff0c;有错误的地方欢迎指正&#xff0c;谢谢! Unity之Unity面试题&#xff08;五&#xff09; TechX 坚持将创新的科技带给世界&#xff01; 拥有更好的学习体验 —— 不断努力&#xff0c;不断进步&#xff0c;不断探索 TechX —— 心探索、心进取…

爬虫逆向实战(40)-某江酒店登陆(AES、MD5)

一、数据接口分析 主页地址&#xff1a;某江酒店 1、抓包 通过抓包可以发现数据接口是/api/member/login 2、判断是否有加密参数 请求参数是否加密&#xff1f; 通过查看“载荷”模块可以发现&#xff0c;有TDFingerprint、blackBoxMd5、password和sw四个加密参数&#x…

Android自定义控件ScrollView实现上下滑动功能

本文实例为大家分享了Android ScrollView实现上下滑动功能的具体代码&#xff0c;供大家参考&#xff0c;具体内容如下 package com.example.zhuang; import android.content.Context; import android.util.AttributeSet; import android.util.DisplayMetrics; import android…

HTML5学习记录

简介 超文本标记语言&#xff08;HyperText Markup Language&#xff0c;简称HTML&#xff09;&#xff0c;是一种用于创建网页的标准标记语言。 编辑器 下载传送门https://code.visualstudio.com/ 下载编辑器插件 标题 标题通过 <h1> - <h6> 标签进行定义。 …

科技助力输电线安全隐患预警,基于YOLOv5全系列参数【n/s/m/l/x】模型开发构建电力设备场景下输电线安全隐患目标检测预警系统

电力的普及让我们的生活变得更加便利&#xff0c;四通八达的电网连接着生活的方方面面&#xff0c;电力能源主要是依托于庞大复杂的电网电力设备进行传输的&#xff0c;有效地保障电网场景下输电线的安全对于保障我们日常生活所需要的电力有着重要的意义&#xff0c;但是电力设…

【Web开发】jquery图片放大镜效果制作变焦镜头图片放大

jquery图片放大镜效果制作变焦镜头图片放大实现 整体步骤流程&#xff1a; 1. 前端html实现 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns"…

【测试开发学习历程】python面向对象

1 面向对象的基本概念 面向对象&#xff1a;是把构成问题事务分解成各个对象&#xff0c;建立对象的目的不是为了完成一个步骤&#xff0c;而是为了描叙某个事物在整个解决问题的步骤中的行为。 怎么理解面向对象&#xff1f; 面向对象是一种编程思想&#xff0c;就是把要解决…

ruoyi-nbcio-plus基于vue3的flowable的自定义业务提交申请组件的升级修改

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 http://122.227.135.243:9666/ 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a…

NAV2 ros galactic安装教程

sudo apt install ros-galactic-gazebo-* 这个必须下载 sudo apt-get install -y ros-galactic-rmw* 这个也是 使用操作系统中的包管理器安装 Nav2 包: sudo apt install ros-galactic-navigation2 sudo apt install ros-galactic-nav2-bringup安装Turtlebot 3 软件包: sudo…