java——线程池

2023-10-29

一、线程池

线程池可以看做是线程的集合。它的工作主要是控制运行的线程的数量,处理过程中将任务放入队列,然后在线程创建后 启动这些任务,如果线程数量超过了最大数量超出数量的线程排队等候,等其它线程执行完毕, 再从队列中取出任务来执行。他的主要特点为:线程复用;控制最大并发数;管理线程。

线程复用:

每一个 Thread 的类都有一个 start 方法。 当调用 start 启动线程时 Java 虚拟机会调用该类的 run 方法。 那么该类的 run() 方法中就是调用了 Runnable 对象的 run() 方法。 我们可以继承重写 Thread 类,在其 start 方法中添加不断循环调用传递过来的 Runnable 对象。 这就是线程池的实 现原理。循环方法中不断获取 Runnable 是用 Queue 实现的,在获取下一个 Runnable 之前可以 是阻塞的。

一般的线程池主要分为以下 4 个组成部分:

  1. 线程池管理器:用于创建并管理线程池
  2. 工作线程:线程池中的线程
  3. 任务接口:每个任务必须实现的接口,用于工作线程调度其运行
  4. 任务队列:用于存放待处理的任务,提供一种缓冲机制

Java 中的线程池是通过 Executor 框架实现的,该框架中用到了 Executor,Executors, ExecutorService,ThreadPoolExecutor ,Callable 和 Future、FutureTask 这几个类。

线程池工作过程 :

  1. 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面
    有任务,线程池也不会马上执行它们。
  2. 当调用 execute() 方法添加一个任务时,线程池会做如下判断:
    a) 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
    b) 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列;
    c) 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要
    创建非核心线程立刻运行这个任务;
    d) 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池
    会抛出异常 RejectExecutionException。
  3. 当一个线程完成任务时,它会从队列中取下一个任务来执行。
  4. 当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运
    行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它
    最终会收缩到 corePoolSize 的大小。

创建和销毁线程所花费的时间和资源可能比处理任务所花费的时间和资源还要多,所以说:我们的线程最好是交由线程池来管理,这样可以减少对线程生命周期的管理,一定程度上提高性能。

二、JDK提供的线程池API

DK给我们提供了Excutor框架来使用线程池,它是线程池的基础

  • Executor提供了一种将**“任务提交”与“任务执行”**分离开来的机制(解耦)

在这里插入图片描述

Executor接口:

public interface Executor {

    /**
     * 执行任务
     */
    void execute(Runnable command);
}

ExcutorService接口:

public interface ExecutorService extends Executor {

    /**
     * 关闭线程池
     */
    void shutdown();

    /**
     * 停止所有主动执行的任务以及停止等待任务的处理,并返回任务列表
     */
    List<Runnable> shutdownNow();

    /**
     * 是否关闭线程池
     */
    boolean isShutdown();

    /**
     * 判断是否所有任务在关闭后全部完成
     */
    boolean isTerminated();

    /**
     * 判断任务执行超时还是提前终止
     * 超时:true
     * 提前终止:false
     */
    boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException;

    /**
     * 提交任务用于执行
     */
    <T> Future<T> submit(Callable<T> task);

    <T> Future<T> submit(Runnable task, T result);

    Future<?> submit(Runnable task);

    /**
     * 执行给定的任务
     */
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
        throws InterruptedException;

    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                                  long timeout, TimeUnit unit)
        throws InterruptedException;

    <T> T invokeAny(Collection<? extends Callable<T>> tasks)
        throws InterruptedException, ExecutionException;

    <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                    long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

AbstractExecutorService类:

对于ExecutorService接口的默认实现,同时新增了三个方法。

public abstract class AbstractExecutorService implements ExecutorService {

   
    protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        return new FutureTask<T>(runnable, value);
    }
    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        return new FutureTask<T>(callable);
    }
    private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks,
                              boolean timed, long nanos)
        throws InterruptedException, ExecutionException, TimeoutException {
        if (tasks == null)
            throw new NullPointerException();
        int ntasks = tasks.size();
        if (ntasks == 0)
            throw new IllegalArgumentException();
        ArrayList<Future<T>> futures = new ArrayList<Future<T>>(ntasks);
        ExecutorCompletionService<T> ecs =
            new ExecutorCompletionService<T>(this);

        // For efficiency, especially in executors with limited
        // parallelism, check to see if previously submitted tasks are
        // done before submitting more of them. This interleaving
        // plus the exception mechanics account for messiness of main
        // loop.

        try {
            // Record exceptions so that if we fail to obtain any
            // result, we can throw the last exception we got.
            ExecutionException ee = null;
            final long deadline = timed ? System.nanoTime() + nanos : 0L;
            Iterator<? extends Callable<T>> it = tasks.iterator();

            // Start one task for sure; the rest incrementally
            futures.add(ecs.submit(it.next()));
            --ntasks;
            int active = 1;

            for (;;) {
                Future<T> f = ecs.poll();
                if (f == null) {
                    if (ntasks > 0) {
                        --ntasks;
                        futures.add(ecs.submit(it.next()));
                        ++active;
                    }
                    else if (active == 0)
                        break;
                    else if (timed) {
                        f = ecs.poll(nanos, TimeUnit.NANOSECONDS);
                        if (f == null)
                            throw new TimeoutException();
                        nanos = deadline - System.nanoTime();
                    }
                    else
                        f = ecs.take();
                }
                if (f != null) {
                    --active;
                    try {
                        return f.get();
                    } catch (ExecutionException eex) {
                        ee = eex;
                    } catch (RuntimeException rex) {
                        ee = new ExecutionException(rex);
                    }
                }
            }

            if (ee == null)
                ee = new ExecutionException();
            throw ee;

        } finally {
            for (int i = 0, size = futures.size(); i < size; i++)
                futures.get(i).cancel(true);
        }
    }
    ......
}

ScheduledExecutorService接口:

在这里插入图片描述

ThreadPoolExecutor类:

这个类是我们用的最多的类,要重点掌握!!!
在这里插入图片描述

ScheduledThreadPoolExecutor类:

相当于提供了“延迟”和“周期性”执行功能的ThreadPoolExecutor类
在这里插入图片描述

2.1 Callable和Future

我们都知道创建线程的两种方式,一是继承Thread类,重写run方法、二是实现Runable接口,重写run方法。

public class TestThread extends Thread{
    @Override
    public void run() {
       
    }
}

----------------
  
public class TestThread implements Runnable{
    
    @Override
    public void run() {
        
    }
}

可以看到run()方法是没有返回值的。那我们想要执行多线程任务后返回给我们任务的结果怎么办?
让我们看看第三种创建线程的方式:实现Callabl接口。重写call方法。
通过上边线程池的简单了解,我们发现:很多的API都有Callable和Future这么两个东西。

Future<?> submit(Runnable task)
<T> Future<T> submit(Callable<T> task)

简单看一下Callable接口,这是个函数式接口,里面只有一个call方法,注释很简单:计算结果,如果无法计算则抛出异常。return的是计算的结果。
在这里插入图片描述

当我们的任务需要返回值的时,我们就可以使用Callable!Future和Callable通常是一起使用的,
它一般我们认为是Callable的返回值,但他其实代表的是任务的生命周期(它是能获取得到Callable的返回值的)。我们看下Future接口的源码:

public interface Future<V> {

    /**
     * 用于停止任务。如果尚未启动,它将停止任务。如果已启动,则仅在mayInterrupt为true时才会中断任务。
     */
    boolean cancel(boolean mayInterruptIfRunning);

    /**
     * 判断任务是否被取消
     * 如果此任务在完成之前已取消则为true
     */
    boolean isCancelled();

    /**
     * 如果任务完成,则返回true,否则返回false
     */
    boolean isDone();

    /**
     * 用于获取任务的结果。如果任务完成,它将立即返回结果,否则将等待任务完成,然后返回结果。
     */
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

Future和Callable示例:

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        
        FutureTask<Integer> future = new FutureTask<>(new CallableDemo(100));

        Thread thread = new Thread(future);
        thread.start();

        //获取任务执行返回值
        Integer result = future.get();

        System.out.println(result);
    }
}
class CallableDemo implements Callable<Integer>{

    private int number;

    public CallableDemo(int number) {
        this.number = number;
    }

    @Override
    public Integer call() {
        int sum = 0;
        for (int x = 1; x <= number; x++) {
            sum += x;
        }
        return sum;
    }
}


console: 5050

FutureTask实现了RunnableFuture接口,而RunnableFuture继承了Runnable和Future,也就是说FutureTask既是Runnable,也是Future。

三、ThreadPoolExecutor详解

ThreadPoolExecutor在构造方法:

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

构造函数的参数含义如下:

  • corePoolSize: 是指线程池中的线程数量,它的数量决定了添加的任务是开辟新的线程去执行,还是放到workQueue任务队列中去,简单理解就是corePoolSize表示允许线程池中允许同时运行的最大线程数;
  • maximumPoolSize: 线程池中最大线程数量**,**这个参数会根据你使用的workQueue任务队列的类型,决定线程池会开辟的最大线程数量;maximumPoolSize肯定是大于等于corePoolSize
  • keepAliveTime: 当线程池中空闲线程数量超过corePoolSize时,多余的线程会在多长时间内被销毁;
  • unit: keepAliveTime的单位;
  • workQueue: 任务队列,被添加到线程池中,但尚未被执行的任务;它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列几种;
  • threadFactory: 线程工厂,用于创建线程,一般用默认即可;
  • handler: 任务拒绝策略,当任务太多来不及处理时,如何拒绝任务;

线程数量要点

  • 如果运行线程的数量少于核心线程数量,则创建新的线程处理请求
  • 如果运行线程的数量大于核心线程数量,小于最大线程数量,则当队列满的时候才创建新的线程
  • 如果核心线程数量等于最大线程数量,那么将创建固定大小的连接池
  • 如果设置了最大线程数量为无穷,那么允许线程池适合任意的并发数量

线程空闲时间要点:

  • 当前线程数大于核心线程数,如果空闲时间已经超过了,那该线程会销毁

排队策略要点

  • 同步移交:不会放到队列中,而是等待线程执行它。如果当前线程没有执行,很可能会新开一个线程执行。
  • 无界限策略:如果核心线程都在工作,该线程会放到队列中。所以线程数不会超过核心线程数
  • 有界限策略:可以避免资源耗尽,但是一定程度上减低了吞吐量

当线程关闭或者线程数量满了和队列饱和了,就有拒绝任务的情况了:
拒绝任务策略:

  • 直接抛出异常
  • 使用调用者的线程来处理
  • 直接丢掉这个任务
  • 丢掉最老的任务

3.1 workQueue任务队列

workQueue任务队列一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列;
1、直接提交队列synchronousQueue: 这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务
2、有界任务队列ArrayBlockingQueue: 基于数组的先进先出队列,此队列创建时必须指定大小;
3、无界任务队列LinkedBlockingQueue: 基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;
4、优先任务队列PriorityBlockingQueue: 优先任务队列通过PriorityBlockingQueue实现

直接提交队列示例:

public class ThreadPoolTest {  
  private static  ExecutorService pool;   
  public static void main(String[] args) {        
 	 pool = new ThreadPoolExecutor(1, 2, 1000, TimeUnit.MILLISECONDS,new SynchronousQueue<Runnable>(), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());        
	  for (int i = 0; i < 3; i++) {        
    	  pool.execute(new ThreadDemo());      
        }  
     }
  }
class ThreadDemo implements Runnable{    
      @Override   
       public void run() {   
            System.out.println(Thread.currentThread().getName());    
      }
}
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.zsn.scheduler.Thread.ThreadDemo@1be6f5c3 rejected from java.util.concurrent.ThreadPoolExecutor@6b884d57[Running, pool size = 2, active threads = 2, queued tasks = 0, completed tasks = 0]	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)	at com.zsn.scheduler.Thread.ThreadPoolTest.main(ThreadPoolTest.java:20)pool-1-thread-1pool-1-thread-2

使用SynchronousQueue队列,提交的任务不会被保存,总是会马上提交执行。如果用于执行任务的线程数量小于maximumPoolSize,则尝试创建新的进程,如果达到maximumPoolSize设置的最大值,则根据你设置的handler执行拒绝策略

有界任务队列示例:

pool = new ThreadPoolExecutor(1, 2, 1000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

使用ArrayBlockingQueue有界任务队列,若有新的任务需要执行时,线程池会创建新的线程,直到创建的线程数量达到corePoolSize时,则会将新的任务加入到等待队列中。若等待队列已满,即超过ArrayBlockingQueue初始化的容量,则继续创建线程,直到线程数量达到maximumPoolSize设置的最大线程数量,若大于maximumPoolSize,则执行拒绝策略。
无界任务队列示例:

pool = new ThreadPoolExecutor(1, 2, 1000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

使用无界任务队列,线程池的任务队列可以无限制的添加新的任务,而线程池创建的最大线程数量就是你corePoolSize设置的数量,也就是说在这种情况下maximumPoolSize这个参数是无效的,哪怕你的任务队列中缓存了很多未执行的任务,当线程池的线程数达到corePoolSize后,就不会再增加了;若后续有新的任务加入,则直接进入队列等待,当使用这种任务队列模式时,一定要注意你任务提交与处理之间的协调与控制,不然会出现队列中的任务由于无法及时处理导致一直增长,直到最后资源耗尽的问题。

优先任务队列PriorityBlockingQueue:
PriorityBlockingQueue它其实是一个特殊的无界队列,它其中无论添加了多少个任务,线程池创建的线程数也不会超过corePoolSize的数量,只不过其他队列一般是按照先进先出的规则处理任务,而PriorityBlockingQueue队列可以自定义规则根据任务的优先级顺序先后执行。

3.2 拒绝策略

线程池中的线程已经用完了,无法继续为新任务服务,同时,等待队列也已经排满了,再也
塞不下新任务了。这时候我们就需要拒绝策略机制合理的处理这个问题。
JDK 内置的拒绝策略如下:

  1. AbortPolicy : 直接抛出异常,阻止系统正常运行。
  2. CallerRunsPolicy : 只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的
    任务。显然这样做不会真的丢弃任务,但是,任务提交线程的性能极有可能会急剧下降。
  3. DiscardOldestPolicy : 丢弃最老的一个请求,也就是即将被执行的一个任务,并尝试再
    次提交当前任务。
  4. DiscardPolicy : 该策略默默地丢弃无法处理的任务,不予任何处理。如果允许任务丢
    失,这是最好的一种方案。
    以上内置拒绝策略均实现了 RejectedExecutionHandler 接口,若以上策略仍无法满足实际
    需要,完全可以自己扩展 RejectedExecutionHandler 接口。

3.3 已默认实现的池

下面我就列举三个比较常见的实现池:

  • newFixedThreadPool
  • newCachedThreadPool
  • SingleThreadExecutor
newFixedThreadPool

newFixedThreadPool是一个固定线程数的线程池,它将返回一个corePoolSize和maximumPoolSize相等的线程池

public static ExecutorService newFixedThreadPool(int nThreads) {        
		return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());    
		}
newCachedThreadPool

创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行 很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用 execute 将重用以前构造 的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并 从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。

public static ExecutorService newCachedThreadPool() {        
 return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,  new SynchronousQueue<Runnable>());    
}
newScheduledThreadPool

创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory) {        
 return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);    
 }
 ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3); 
 	scheduledThreadPool.schedule(newRunnable(){         
 	@Override         
 	public void run() {         
 		System.out.println("延迟三秒");         
 		} 	
 	}, 3, TimeUnit.SECONDS);	
 	scheduledThreadPool.scheduleAtFixedRate(newRunnable(){ 		
 	@Override 		
 	public void run() { 		
 	System.out.println("延迟 1 秒后每三秒执行一次"); 		
 		} 	
 	},1,3,TimeUnit.SECONDS);  
newSingleThreadExecutor

Executors.newSingleThreadExecutor()返回一个线程池(这个线程池只有一个线程),这个线程池可以在线程死后(或发生异常时)重新启动一个线程来替代原来的线程继续执行下去!

public static ExecutorService newSingleThreadExecutor() {        
return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));}

四、execute执行方法

public void execute(Runnable command) {        
	if (command == null) throw new NullPointerException();        
	int c = ctl.get();		
	//如果线程池中运行的线程数量<corePoolSize,则创建新线程来处理请求,即使其他辅助线程是空闲的。        
	if (workerCountOf(c) < corePoolSize) {            
		if (addWorker(command, true)) return;            
		c = ctl.get();        
	}		
	//如果线程池中运行的线程数量>=corePoolSize,且线程池处于RUNNING状态,且把提交的任务成功放入阻塞队列中,就再次检查线程池的状态,			
	// 1.如果线程池不是RUNNING状态,且成功从阻塞队列中删除任务,则该任务由当前 RejectedExecutionHandler 处理。			
	// 2.否则如果线程池中运行的线程数量为0,则通过addWorker(null, false)尝试新建一个线程,新建线程对应的任务为null。        
	if (isRunning(c) && workQueue.offer(command)) {            
		int recheck = ctl.get();            
		if (! isRunning(recheck) && remove(command)) reject(command);           
		else if (workerCountOf(recheck) == 0) addWorker(null, false);        
	   }		
		// 如果以上两种case不成立,即没能将任务成功放入阻塞队列中,且addWoker新建线程失败,则该任务由当前 RejectedExecutionHandler 处理。        
		else if (!addWorker(command, false))reject(command);
		}

五、线程池关闭

ThreadPoolExecutor提供了shutdown()和shutdownNow()两个方法来关闭线程池

  • 调用shutdown()后,线程池状态立刻变为SHUTDOWN,而调用shutdownNow(),线程池状态立刻变为STOP
  • shutdown()等待任务执行完才中断线程,而shutdownNow()不等任务执行完就中断了线程。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java——线程池 的相关文章

随机推荐

  • 13-Error接口错误处理以及go Module

    Error接口和错误处理 Go语言中的错误处理和其他语言不一样 它把错误当成一种值来处理 更强调判断错误 处理错误 而不是一股脑的catch错误 error接口 Go语言中把错误当成一种特殊的值来处理 不支持其他语言的try catch 捕
  • 【matlab】常用快捷键汇总

    记录自己常用的快捷键 冲冲冲 命令行窗口的常用快捷键 上下光标键 调用Matlab最近使用过的历史命令 便于快速重新执行 编辑器的常用快捷键 Ctrl R 快速注释代码段 拖动鼠标选中需要注释的代码行 Ctrl T 快速取消注释代码段 ht
  • Linux命令之tftp常用参数说明

    tftp 文件传输 实际操作 put 上传 get 下载 mode 文件传输模式 connect 连接 quit 退出 参数 g 下载文件 p 上传文件 l 本地文件名 r 远程主机文件名 常用命令 tftp g r a 10 0 0 1
  • ROS2 驱动思岚G4雷达(ydlidar)- Rviz显示

    记录G4雷达的配置 系统环境为 Ubuntu22 04 配置步骤 1 安装雷达SDK 2 构建 G4 雷达 ROS2 项目工程文件 3 使用Rviz可视化界面显示 1 安装雷达SDK 1 1 安装CMake YDLidar SDK需要CMa
  • 从服务器上复制文件复制不出来的,从远程服务器上复制文件

    从远程服务器上复制文件 内容精选 换一换 问题描述 将在x86平台上经过sh加密的XXX sh x文件复制到基于鲲鹏的服务器中 无法执行 例如 将test sh x从x86平台复制鲲鹏平台云服务器执行 test sh x回显内容如下 问题原
  • 毕设分享javaWeb的网络考试系统的设计与实现

    文章目录 1 项目简介 2 实现效果 3 系统设计 4 关键代码 5 论文概览 6 最后 1 项目简介 Hi 各位同学好呀 这里是L学长 今天向大家分享一个今年 2022 最新完成的毕业设计项目作品 毕设分享javaWeb的网络考试系统的设
  • C#this关键字的四种用法

    用法一 this代表当前类的实例对象 namespace Demo public class Test private string scope 全局变量 public string getResult string scope 局部变量
  • sqli-labs:less-32(宽字节注入)

    宽字节注入 产生原因 1 mysql 在使用 GBK 编码的时候 会认为两个字符为一个汉字 例如 aa 5c 就是一个汉字 前一个 ascii 码大于 128 才能到汉字的范围 2 mysqli real escape string 函数转
  • Helix QAC — 软件静态测试工具

    Helix QAC 是Perforce 公司 原PRQA 公司 产品 主要用于C C 代码的完全自动化静态分析工作 可以提供编码规则检查 代码质量度量 软件结构分析 测试结果管理等功能 Helix QAC 能够全面而正确地发现软件中潜在的问
  • Jmeter系列(33)- 跨平台运行 Jmeter,CSV 文件路径如何设置?

    抛出问题 上一篇文章中详细讲解了 CSV 数据文件设置的用法 https www cnblogs com poloyy 通常 我们编写 调试脚本都是在 Window 机器上 而真正性能测试时 脚本几乎都在 Linux 下运行 使用 CSV
  • 图像处理中 光场(Light Field)简介及理解

    1 光场 Light Field 是一个四维的参数化表示 是空间中同时包含位置和方向信息的四维光辐射场 简单地说 涵盖了光线在传播中的所有信息 光线携带二维位置信息 u v 和二维方向信息 x y 在光场中传递 光场是光线在空间传播中四维的
  • 语法怎么学

    为什么要学非谓语动词 太常见了 非谓语动词可以做句子中 除了谓语的任何成分 是一个非常重要课题 弄懂了这个 就相当于弄懂了语法的90 特别是语法题 怎样学习 我在小破站上查了不少教程 大多数都很好 但不太适合新手 讲得很复杂 我看到一个视频
  • 【Bugly干货分享】手把手教你逆向分析 Android 程序

    很多人写文章 喜欢把什么行业现状啊 研究现状啊什么的写了一大通 感觉好像在写毕业论文似的 我这不废话 先直接上几个图 感受一下 第一张图是在把代码注入到地图里面 启动首页的时候弹出个浮窗 下载网络的图片 苍老师你们不会不认识吧 第二张图是微
  • Pygame实战:Python做一款超好玩的滑雪大冒险小游戏,超会玩【附源码】

    导语 冬日当然要和心爱的人一起去滑雪 徜徉在雪白的世界 浪漫又刺激 唯有爱和滑雪不可辜负 不但风景绝美 而且还超 会 玩 现在还不是时候 但秋天已过半动冬天还会远吗 既然不能现在去滑雪 但是小编可以先让大家身临其境 带大家做一款超好玩的滑雪
  • Kafka3.0.0版本——消费者(RoundRobin分区分配策略以及再平衡)

    目录 一 RoundRobin 分区分配策略原理 二 RoundRobin分区分配策略代码案例 2 1 创建带有7个分区的sixTopic主题 2 3 创建三个消费者 组成 消费者组 2 3 创建生产者 2 4 测试 2 5 RoundRo
  • Latex中一些特殊常用符号的输入

    搞学术的童鞋们很有可能会接触到Latex这种论文格式编辑工具 一般在论文投稿的时候 大多数期刊和会议会给一个Latex模板 要求将你的论文用Latex编辑成 pdf版本 一般的word文字部分是可以直接复制粘贴到latex的 tex文档中的
  • QT子线程中使用主线程的方法

    QT子线程中使用主线程的方法 QMetaObject invokeMethod p setText Q ARG const QString strNum 使用QMetaObject invokeMethod方法将setText函数在主线程运
  • 机器学习——信息熵与信息增益

    问 信息熵越大说明其纯度越高 答 错误 信息熵越小 说明数据集的纯度越高 信息熵是衡量数据集纯度的指标之一 它是对数据集中所有类别出现概率进行加权平均所得到的一个值 信息熵是度量样本集合纯度 不确定度最常用的指标之一 但要注意 信息熵越小
  • 【Mac】mac安装redis客户端 Error: Cask ‘rdm‘ is unavailable: No Cask with this name exist

    1 概述 mac安装redis客户端 rdm 报错如下 lcc lcc brew cask install rdm Updating Homebrew Error Cask rdm is unavailable No Cask with t
  • java——线程池

    一 线程池 线程池可以看做是线程的集合 它的工作主要是控制运行的线程的数量 处理过程中将任务放入队列 然后在线程创建后 启动这些任务 如果线程数量超过了最大数量超出数量的线程排队等候 等其它线程执行完毕 再从队列中取出任务来执行 他的主要特