ThreadPoolExecutor类讲解

2023-11-06

一、ThreadPoolExecutor类讲解
1、线程池状态:
五种状态:

线程池 的状态 说明
RUNNING 允许提交并处理任务
SHUTDOWN 不允许提交新的任务,但是会处理完已提交的任务
STOP

不允许提交新的任务,也不会处理阻塞队列中未执行的任务,

并设置正在执行的线程的中断标志位

TIDYING 所有任务执行完毕,池中工作的线程数为0,等待执行terminated()勾子方法
TERMINATED terminated()勾子方法执行完毕

线程池的shutdown() 方法,将线程池由 RUNNING(运行状态)转换为 SHUTDOWN状态
线程池的shutdownNow()方法,将线程池由RUNNING 或 SHUTDOWN 状态转换为 STOP 状态。
注:SHUTDOWN 状态 和 STOP 状态 先会转变为 TIDYING 状态,最终都会变为 TERMINATED

2、ThreadPoolExecutor构造函数:
ThreadPoolExecutor继承自AbstractExecutorService,而AbstractExecutorService实现了ExecutorService接口。

接下来我们分别讲解这些参数的含义。

2.1)线程池工作原理:

corePoolSize :线程池中核心线程数的最大值
maximumPoolSize :线程池中能拥有最多线程数
workQueue:用于缓存任务的阻塞队列
当调用线程池execute() 方法添加一个任务时,线程池会做如下判断:

如果有空闲线程,则直接执行该任务;
如果没有空闲线程,且当前运行的线程数少于corePoolSize,则创建新的线程执行该任务;
如果没有空闲线程,且当前的线程数等于corePoolSize,同时阻塞队列未满,则将任务入队列,而不添加新的线程;
如果没有空闲线程,且阻塞队列已满,同时池中的线程数小于maximumPoolSize ,则创建新的线程执行任务;
如果没有空闲线程,且阻塞队列已满,同时池中的线程数等于maximumPoolSize ,则根据构造函数中的 handler 指定的策略来拒绝新的任务。

2.2)KeepAliveTime:

 keepAliveTime :表示空闲线程的存活时间
TimeUnit unit :表示keepAliveTime的单位
当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。

注:如果线程池设置了allowCoreThreadTimeout参数为true(默认false),那么当空闲线程超过keepaliveTime后直接停掉。(不会判断线程数是否大于corePoolSize)即:最终线程数会变为0。

2.3)workQueue 任务队列:

workQueue :它决定了缓存任务的排队策略
ThreadPoolExecutor线程池推荐了三种等待队列,它们是:SynchronousQueue 、LinkedBlockingQueue 和 ArrayBlockingQueue。

1)有界队列:

SynchronousQueue :一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于 阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法 Executors.newCachedThreadPool 使用了这个队列。
ArrayBlockingQueue:一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。一旦创建了这样的缓存区,就不能再增加其容量。试图向已满队列中放入元素会导致操作受阻塞;试图从空队列中提取元素将导致类似阻塞。
2)无界队列:

LinkedBlockingQueue:基于链表结构的无界阻塞队列,它可以指定容量也可以不指定容量(实际上任何无限容量的队列/栈都是有容量的,这个容量就是Integer.MAX_VALUE)
PriorityBlockingQueue:是一个按照优先级进行内部元素排序的无界阻塞队列。队列中的元素必须实现 Comparable 接口,这样才能通过实现compareTo()方法进行排序。优先级最高的元素将始终排在队列的头部;PriorityBlockingQueue 不会保证优先级一样的元素的排序。
注意:keepAliveTime和maximumPoolSize及BlockingQueue的类型均有关系。如果BlockingQueue是无界的,那么永远不会触发maximumPoolSize,自然keepAliveTime也就没有了意义。

2.4)threadFactory:

threadFactory :指定创建线程的工厂。(可以不指定)
如果不指定线程工厂时,ThreadPoolExecutor 会使用ThreadPoolExecutor.defaultThreadFactory 创建线程。默认工厂创建的线程:同属于相同的线程组,具有同为 Thread.NORM_PRIORITY 的优先级,以及名为 “pool-XXX-thread-” 的线程名(XXX为创建线程时顺序序号),且创建的线程都是非守护进程。

2.5)handler 拒绝策略:

handler :表示当 workQueue 已满,且池中的线程数达到 maximumPoolSize 时,线程池拒绝添加新任务时采取的策略。(可以不指定)

策略 BB
ThreadPoolExecutor.AbortPolicy() 抛出RejectedExecutionException异常。默认策略
ThreadPoolExecutor.CallerRunsPolicy() 由向线程池提交任务的线程来执行该任务
ThreadPoolExecutor.DiscardPolicy() 抛弃当前的任务
ThreadPoolExecutor.DiscardOldestPolicy() 抛弃最旧的任务(最先提交而没有得到执行的任务)

最科学的的还是 AbortPolicy 提供的处理方式:抛出异常,由开发人员进行处理。

3、常用方法:
除了在创建线程池时指定上述参数的值外,还可在线程池创建以后通过如下方法进行设置。

此外,还有一些方法:

getCorePoolSize():返回线程池的核心线程数,这个值是一直不变的,返回在构造函数中设置的coreSize大小;
getMaximumPoolSize():返回线程池的最大线程数,这个值是一直不变的,返回在构造函数中设置的coreSize大小;
getLargestPoolSize():记录了曾经出现的最大线程个数(水位线);
getPoolSize():线程池中当前线程的数量;
getActiveCount():Returns the approximate(近似) number of threads that are actively executing tasks;
prestartAllCoreThreads():会启动所有核心线程,无论是否有待执行的任务,线程池都会创建新的线程,直到池中线程数量达到 corePoolSize;
prestartCoreThread():会启动一个核心线程(同上);
allowCoreThreadTimeOut(true):允许核心线程在KeepAliveTime时间后,退出;
4、Executors类:
Executors类的底层实现便是ThreadPoolExecutor! Executors 工厂方法有:

Executors.newCachedThreadPool():无界线程池,可以进行自动线程回收
Executors.newFixedThreadPool(int):固定大小线程池
Executors.newSingleThreadExecutor():单个后台线程
它们均为大多数使用场景预定义了设置。不过在阿里java文档中说明,尽量不要用该类创建线程池。

二、线程池相关接口介绍:
1、ExecutorService接口:
该接口是真正的线程池接口。上面的ThreadPoolExecutor以及下面的ScheduledThreadPoolExecutor都是该接口的实现类。改接口常用方法:

Future<?> submit(Runnable task):提交Runnable任务到线程池,返回Future对象,由于Runnable没有返回值,也就是说调用Future对象get()方法返回null;
<T> Future<T> submit(Callable<T> task):提交Callable任务到线程池,返回Future对象,调用Future对象get()方法可以获取Callable的返回值;
<T> Future<T> submit(Runnable task,T result):提交Runnable任务到线程池,返回Future对象,调用Future对象get()方法可以获取Runnable的参数值;
invokeAll(collection of tasks)/invokeAll(collection of tasks, long timeout, TimeUnit unit):invokeAll会按照任务集合中的顺序将所有的Future添加到返回的集合中,该方法是一个阻塞的方法。只有当所有的任务都执行完毕时,或者调用线程被中断,又或者超出指定时限时,invokeAll方法才会返回。当invokeAll返回之后每个任务要么返回,要么取消,此时客户端可以调用get/isCancelled来判断具体是什么情况。
invokeAny(collection of tasks)/invokeAny(collection of tasks, long timeout, TimeUnit unit):阻塞的方法,不会返回 Future 对象,而是返回集合中某一个Callable 对象的结果,而且无法保证调用之后返回的结果是哪一个 Callable,如果一个任务运行完毕或者抛出异常,方法会取消其它的 Callable 的执行。和invokeAll区别是只要有一个任务执行完了,就把结果返回,并取消其他未执行完的任务;同样,也带有超时功能;
shutdown():在完成已提交的任务后关闭服务,不再接受新任;
shutdownNow():停止所有正在执行的任务并关闭服务;
isTerminated():测试是否所有任务都执行完毕了;
isShutdown():测试是否该ExecutorService已被关闭。
1.1)submit方法示例:

我们知道,线程池接口中有以下三个主要方法,接下来我们看一下具体示例:

1)Callable:

public static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 50, 300, TimeUnit.SECONDS, 
            new ArrayBlockingQueue<Runnable>(50),  
            new ThreadFactory(){ public Thread newThread(Runnable r) {
                return new Thread(r, "schema_task_pool_" + r.hashCode());
            }}, new ThreadPoolExecutor.DiscardOldestPolicy());
 
public static void callableTest() {
    int a = 1;
    //callable
    Future<Boolean> future = threadPool.submit(new Callable<Boolean>(){
        @Override
        public Boolean call() throws Exception {
            int b = a + 100;
            System.out.println(b);
            return true;
        }
    });
    try {
        System.out.println("feature.get");
        Boolean boolean1 = future.get();
        System.out.println(boolean1);
    } catch (InterruptedException e) {
        System.out.println("InterruptedException...");
        e.printStackTrace();
    } catch (ExecutionException e) {
        System.out.println("execute exception...");
        e.printStackTrace();
    } 
}


2)Runnable:

public static void runnableTest() {
    int a = 1;
    //runnable
    Future<?> future1 = threadPool.submit(new Runnable(){
        @Override
        public void run() {
            int b = a + 100;
            System.out.println(b);
        }
    });
    try {
        System.out.println("feature.get");
        Object x = future1.get(900,TimeUnit.MILLISECONDS);
        System.out.println(x);//null
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        System.out.println("execute exception...");
        e.printStackTrace();
    } catch (TimeoutException e) {
        e.printStackTrace();
    }
}


3)Runnable+result:

class RunnableTask implements Runnable {
    Person p;
    RunnableTask(Person p) {
        this.p = p;
    }
 
    @Override
    public void run() {
        p.setId(1);
        p.setName("Runnable Task...");
    }
}
class Person {
    private Integer id;
    private String name;
    
    public Person(Integer id, String name) {
        super();
        this.id = id;
        this.name = name;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Person [id=" + id + ", name=" + name + "]";
    }
}
 
public static void runnableTest2() {
    //runnable + result
    Person p = new Person(0,"person");
    Future<Person> future2 = threadPool.submit(new RunnableTask(p),p);
    try {
        System.out.println("feature.get");
        Person person = future2.get();
        System.out.println(person);
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
}


1.2)线程池执行时,Callable的call方法(Runnable的run方法)抛出异常后,会出现什么?

在上面的例子中我们可以看到,线程池无论是执行Callable还是Runnable,调用返回的Future对象get()方法时需要处理两种异常(如果是调用get(timeout)方法,需要处理三种异常),如下:

//在线程池上运行
Future<Object> future = threadPool.submit(callable);
try {
    System.out.println("feature.get");
    Object x = future.get(900,TimeUnit.MILLISECONDS);
    System.out.println(x);
} catch (InterruptedException e) {
    e.printStackTrace();
} catch (ExecutionException e) {
    System.out.println("execute exception...");
    e.printStackTrace();
} catch (TimeoutException e) {
    e.printStackTrace();
}

如果get方法被打断,进入InterruptedException异常;
如果线程执行过程(call、run方法)中抛出异常,进入ExecutionException异常;
如果get方法超时,进入TimeoutException异常;
1.3)submit()和execute()方法区别:

ExecutorService、ScheduledExecutorService接口的submit()和execute()方法都是把任务提交到线程池中,但二者的区别是

接收的参数不一样,execute只能接收Runnable类型、submit可以接收Runnable和Callable两种类型;
submit有返回值,而execute没有返回值;submit方便Exception处理;
1)submit方法内部实现:

其实submit方法也没有什么神秘的,就是将我们的任务封装成了RunnableFuture接口(继承了Runnable、Future接口),再调用execute方法,我们看源码:

   

public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);  //转成 RunnableFuture,传的result是null
        execute(ftask);
        return ftask;
    }
 
    public <T> Future<T> submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }
 
    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }


2)newTaskFor方法内部实现:

newTaskFor方法是new了一个FutureTask返回,所以三个方法其实都是把task转成FutureTask,如果task是Callable,就直接赋值,如果是Runnable 就转为Callable再赋值。

当submit参数是Callable 时:

   

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;      
    }


当submit参数是Runnable时:

 // 按顺序看,层层调用
    protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        return new FutureTask<T>(runnable, value);
    }
    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);  //转 runnable 为 callable 
        this.state = NEW; 
    }
   // 以下为Executors中的方法
    public static <T> Callable<T> callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter<T>(task, result);
    }
    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;
        }
    }


看了源码就揭开了神秘面纱了,就是因为Future需要返回结果,所以内部task必须是Callable,如果task是Runnable 就偷天换日,在Runnable 外面包个Callable马甲,返回的结果在构造时就写好。

参考:搞懂Runnable、Callable、Future、FutureTask 及应用_赶路人儿的博客-CSDN博客

1.4)ScheduledExecutorService接口:

继承ExecutorService,并且提供了按时间安排执行任务的功能,它提供的方法主要有:

schedule(task, initDelay): 安排所提交的Callable或Runnable任务在initDelay指定的时间后执行;
scheduleAtFixedRate():安排所提交的Runnable任务按指定的间隔重复执行;
scheduleWithFixedDelay():安排所提交的Runnable任务在每次执行完后,等待delay所指定的时间后重复执行;
注:该接口的实现类是ScheduledThreadPoolExecutor。

2、Callable接口:
jdk1.5以后创建线程可以通过一下方式:

继承Thread类,实现void run()方法;
实现Runnable接口,实现void run()方法;
实现Callable接口,实现V call() Throws Exception方法
1)Callable和Runnale接口区别:

Callable可以抛出异常,和Future、FutureTask配合可以用来获取异步执行的结果;
Runnable没有返回结果,异常只能内部消化;
2)执行Callable的线程的方法可以通过以下两种方式:

借助FutureTask,使用Thread的start方法来执行;
加入到线程池中,使用线程池的execute或submit执行;
注:Callable无法直接使用Thread来执行;

我们都知道,Callable带有返回值的,如果我们不需要返回值,却又想用Callable该如何做?

jdk中有个Void类型(大写V),但必须也要return null。

threadpool.submit(new Callable<Void>() {
    @Override
    public Void call() {
        //...
        return null;
    }
});
3)通过Executors工具类可以把Runnable接口转换成Callable接口:

Executors中的callable方法可以将Runnable转成Callable,如下:

public static <T> Callable<T> callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter<T>(task, result);
}
RunnableAdapter类在上面已经看过源码,原理就是将返回值result作为成员变量,通过参数传递进去,进而实现了Runnable可以返回值。

示例:

public static void test5() {
        Person p = new Person(0,"person");
        RunnableTask runnableTask = new RunnableTask(p);//创建runnable
        Callable<Person> callable = Executors.callable(runnableTask,p);//转换
        Future<Person> future1 = threadPool.submit(callable);//在线程池上执行Callable
        try {
            Person person = future1.get();
            System.out.println(person);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        
        Runnable runnable = new Runnable() {//创建Runnable
            @Override
            public void run() {
                
            }
        };
        Callable<Object> callable2 = Executors.callable(runnable);//转换
        Future<Object> future2 = threadPool.submit(callable2);//在线程池上执行Callable
        try {
            Object o = future2.get();
            System.out.println(o);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }


3、Future接口:
3.1)Future是用来获取异步计算结果的接口,常用方法:

boolean cancel(boolean mayInterruptIfRunning):试图取消对此任务的执行。如果任务已完成、或已取消,或者由于某些其他原因而无法取消,则此尝试将失败。当调用 cancel 时,如果调用成功,而此任务尚未启动,则此任务将永不运行。如果任务已经启动,则 mayInterruptIfRunning 参数确定是否应该以试图停止任务的方式来中断执行此任务的线程。此方法返回后,对 isDone() 的后续调用将始终返回 true。如果此方法返回 true,则对 isCancelled() 的后续调用将始终返回 true。
boolean isCancelled():如果在任务正常完成前将其取消,则返回 true。
boolean isDone():如果任务已完成,则返回 true,可能由于正常终止、异常或取消而完成,在所有这些情况中,此方法都将返回 true。
V get()throws InterruptedException,ExecutionException:获取异步结果,此方法会一直阻塞等到计算完成;
V get(long timeout,TimeUnit unit) throws InterruptedException,ExecutionException,TimeoutException:获取异步结果,此方法会在指定时间内一直阻塞等到计算完成,超时后会抛出超时异常。
通过方法分析我们也知道实际上Future提供了3种功能:

能够中断执行中的任务;
判断任务是否执行完成;
获取任务执行完成后额结果。
但是Future只是一个接口,我们无法直接创建对象,因此就需要其实现类FutureTask登场啦。

3.2)FutureTask类:

1)FutureTask类的实现:

public class FutureTask<V> implements RunnableFuture<V> {
//...
}
 
public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}


FutureTask实现了Runnable、Future两个接口。由于FutureTask实现了Runnable,因此它既可以通过Thread包装来直接执行,也可以提交给ExecuteService来执行。并且还可以直接通过get()函数获取执行结果,该函数会阻塞,直到结果返回。因此FutureTask既是Future、Runnable,又是包装了Callable( 如果是Runnable最终也会被转换为Callable ), 它是这两者的合体。

2)FutureTask的构造函数:

public FutureTask(Callable<V> callable) {
 
}
 
public FutureTask(Runnable runnable, V result) {
 
}
3.3)示例:(FutureTask两种构造函数、以及在Thread和线程池上运行)

1)FutureTask包装过的Callable在Thread、线程池上执行:

public static void test3() {
        int a = 1,b = 2;
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return a + b;
            }
        };
        //通过futureTask来执行Callable
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        
        //1.使用Thread执行线程
        new Thread(futureTask).start();
        try {
            Integer integer = futureTask.get();
            System.out.println(integer);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        
        //2.使用线程池执行线程
        Executors.newFixedThreadPool(1).submit(futureTask);
        threadPool.shutdown();
        try {
            Integer integer = futureTask.get();
            System.out.println(integer);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } 
    }


2)FutureTask包装过的Runnable在Thread、线程池上执行:

public static void test4() {
        Person p = new Person(0,"person");
        RunnableTask runnableTask = new RunnableTask(p);
        
        //创建futureTask来执行Runnable
        FutureTask<Person> futureTask = new FutureTask<>(runnableTask,p);
        
        //1.使用Thread执行线程
        new Thread(futureTask).start();
        try {
            Person x = futureTask.get();
            System.out.println(x);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } 
        
        //2.使用线程池执行线程
        threadPool.submit(futureTask);
        threadPool.shutdown();
        try {
            Person y = futureTask.get();
            System.out.println(y);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }


Person、RunnableTask类同上面的示例中。
 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ThreadPoolExecutor类讲解 的相关文章

  • 从 OMElement 对象获取 InputStream/io.Reader

    我有一个OMElement对象 从中我想得到一个InputStream或读者对象 我想要的是流式传输xml来自OMElement我有 没有加载到内存中 我只能得到XMLStreamReader对此表示反对 但我找不到办法得到InputStr
  • Java/JAXB:将具有相同名称但不同属性值的 XML 元素解组到不同的类成员

    我正在尝试根据其属性之一将具有多个 Fields 元素的 XML 解析为不同的类成员 这是 XML
  • JUnit Eclipse 显示 System.out.print() 的

    我正在使用 JUnit 3 和 Eclipse 3 4 当我运行 JUnit 测试用例时 一切正常并且测试完美完成 唯一的事情是我想查看我正在运行的类的输出 所有类都具有一些输出值的基本 System out print 因此 当我运行测试
  • @PreUpdate 不适用于 Spring Data JPA

    我有一个实体 Entity EntityListeners MyEntityListener class class MyEntity 还有听者 class MyEntityListener PrePersist PreUpdate pub
  • 如何正确配置Tomcat SSLHostConfig?

    我正在按照本教程在 tomcat 中启用 ssl https medium com raupach how to install lets encrypt with tomcat 3db8a469e3d2 https medium com
  • 无法从后台服务通过 WiFi 访问互联网

    我将直接介绍我发现的一些事实 数据 如果您遇到 解决了类似的问题 请帮助我 我每 5 分钟向服务器发送一次数据 除非用户在服务器的帮助下手动将其关闭 wakeful broadcast receiver通过一个intent service
  • 如何模拟一个方面

    我目前正在使用aspectj 开发一些监控工具 因为这个工具应该是技术独立的 尽可能 所以我没有使用 Spring 进行注入 但我希望我的方面能够经过单元测试 方面示例 Aspect public class ClassLoadAspect
  • Java 的 QP 求解器 [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • getClassLoader().getResource() 返回 null

    我有这个测试应用程序 import java applet import java awt import java net URL public class Test extends Applet public void init URL
  • Vertx HttpClient getNow 不工作

    我的 vertx HttpClient 有问题 下面的代码显示使用 vertx 和纯 java 测试 GET Vertx vertx Vertx vertx HttpClientOptions options new HttpClientO
  • 您能让 Tomcat 6 stdout.log 文件表现得像 log4j DailyRollingFileAppender 吗?

    我们使用的是 Tomcat 6 的 Windows 安装 默认情况下 我们应用程序的 log4j 输出将转到 catalina base logs stdout log 文件 该日志文件仅在我们重新启动 Tomcat 时滚动 并且文件名始终
  • 如何使用 Java 原生接口从 Java 调用 Go 函数?

    可以通过以下方式调用 C 方法JNA https en wikipedia org wiki Java Native AccessJava 中的接口 如何使用 Go 实现相同的功能 package main import fmt impor
  • 当容器大小更改时,JTable 仅调整选定列的大小

    对于面板内的 JTable 如果面板变大 我如何将额外的空间仅分配给某些列 在我的例子中 分配给最后一列 尽管提供 第 3 4 列和8 将获得额外的空间 我想允许用户手动更改所有列的列大小 我尝试了 table setAutoResizeM
  • 是什么原因导致“对象不是声明类的实例”? [复制]

    这个问题在这里已经有答案了 可能的重复 使用反射调用方法时 为什么会出现 对象不是声明类的实例 https stackoverflow com questions 7202988 why do i get object is not an
  • 如何计算文件中单词的长度?爪哇

    我正在尝试编写一个代码来计算文件中特定长度的单词数 例如 How are you 会打印 Proportion of 3 letter words 100 3 words 我想计算长度为 1 2 3 4 5 6 7 8 9 10 11 12
  • Mule/码头设置

    我有一个正在运行的 Mule 应用程序 我想在其上设置 Jetty 来响应 http 请求 以下配置
  • 将字符串中的字符向左移动

    我是 Stack Overflow 的新手 有一道编程课的实验室问题一直困扰着我 该问题要求我们将字符串 s 的元素向左移动 k 次 例如 如果输入是 Hello World 和3 它将输出 lo WorldHel 对于非常大的 k 值 它
  • JDK 7 的快速调试/调试构建

    我正在寻找 JDK 的调试 或者我猜他们称之为快速调试构建 以启用在运行时生成的打印程序集以及查找性能问题时所需的其他诊断 就目前情况而言 我似乎找不到可以直接使用的 现成的 快速调试构建二进制包 有人可以帮我提供下载链接 或者至少提供有关
  • 日期时间解析异常

    解析日期时 我的代码中不断出现异常错误 日期看起来像这样 Wed May 21 00 00 00 EDT 2008 这是尝试读取它的代码 DateTimeFormatter formatter DateTimeFormatter ofPat
  • Java 9 中紧凑字符串和压缩字符串的区别

    有什么优点紧凑的字符串 http openjdk java net jeps 254JDK9 中的压缩字符串 压缩字符串 Java 6 和紧凑字符串 Java 9 都有相同的动机 字符串通常实际上是 Latin 1 因此浪费了一半的空间 和

随机推荐

  • 数据要素流通视角下数据安全保障研究报告

    报告围绕数据要素流通视角下流通数据 流通活动 流通设施的安全需求 分析健全我国数据安全保障体系的推进思路 并从分类分级 流通环境 安全技术 协同共治等方面提出措施建议 为完善我国数据要素流通视角下数据安全保障提供有益参考与借鉴 关注公众号
  • WinCE5.0显卡驱动修改笔记

    WinCE5 0显卡驱动修改笔记公司前段时间让我在Geode上安装一个CE5 0 我把系统安装好之后发现显卡驱动不支持开发板的屏幕 我们的屏幕是800x480的 所以我只能自己动手写修改了一下驱动让它能够支持800x480 一下是我对驱动的
  • python报错code for hash md5 was not found解决方案

    因为开发机服务器不能上网 只能手动安装Python 但是装完后import hashlib出现异常 出现不支持sha256 sha512 md5等错误 现象如下 gt gt gt import hashlib ERROR root code
  • 排序算法之时间复杂度为O(N^2)的算法

    背景知识 排序算法算是比较基础的算法了 但是在面试过程中偶尔也会被问到 虽然很多语言都内置了排序函数 例如php的sort函数等等 但是还是有必要聊聊排序算法 这篇文章中将介绍时间复杂度为O N 2 的几个排序算法 本文基于从小到大排序讲解
  • react面试题(30个)

    1 React Native相对于原生的ios和Android有哪些优势 react native一套代码可以开发出跨平台app 减少了人力 节省了时间 避免了 iOS 与 Android 版本发布的时间差 开发新功能可以更迅速 等等 2
  • go语言后端调用以太坊rpc

    任务要求 使用golang作为后端语言 获取eth 私链 中的账户信息以及创建新的账号 1 启动geth geth identity aaron datadir data0 rpcport 8545 rpccorsdomain port 3
  • 分布式工程团队建设的十大教训

    转自 https www zybuluo com lsmn note 1059823 摘要 人才招聘 培养并促进分布式工程团队的发展并非一日之功 但是值得投资 Bruno提出了一些非常重要的见解 揭示了如何让团队全力以赴 而不管地理位置在哪
  • 初识springBoot

    springboot初学应该了解哪些 了解更多请看Spring Boot 初识 系列 会持续更新 Spring Boot 初识丨一 入门实战 Spring Boot 初识丨二 maven Spring Boot 初识丨三 starter S
  • springcloud搭建标配配置(参考)

    文章目录 架构图 shop parent 后端父项目 pom xml shop common 公共项目 pom xml CommonConstants UserInfo 用户对象 BusinessException 自定义异常 Common
  • SpringBoot+WebSocket+Netty实现消息推送

    实现思路 前端使用webSocket与服务端创建连接的时候 将用户ID传给服务端 服务端将用户ID与channel关联起来存储 同时将channel放入到channel组中 如果需要给所有用户发送消息 直接执行channel组的writeA
  • FusionSphere华为服务器虚拟化解决方案定位、架构、原理、应用场景

    目录 定位 应用场景 架构 原理 定位 华为fusion sphere虚拟化套件是业界领先的虚拟化解决方案 能够帮助客户解决数据中心基础设施的资源利用率低 业务上线周期时间长 数据中心能耗高等问题 应用场景 1 从应用侧来看 可应用于帮助客
  • 配置控制(自用)

    wd 123123 snh 123123
  • android开发之代理Window.Callback

    Window Callback是window类的一个内部接口 该接口包含了一系列类似于dispatchXXX和onXXX的接口 当window接收到外界状态改变的通知时 就会回调其中的相应方法 比如 当用户点击某个控件时 就会回调Windo
  • enncy-admin ant design vue 后台管理系统脚手架

    github 项目地址 https github com enncy enncy admin vue3 版本的请看我的另一个项目 https github com enncy funny blog admin 在 template 分支你可
  • Hyper-V服务开启or关闭

    1 概念 Hyper V服务是一个微软的虚拟机 所以如果要在windows上启动虚拟机的话 先需要把Hyper V服务功能关闭 2 Hyper V服务关闭 以管理员身份运行命令提示符 执行以下命令 bcdedit set hyperviso
  • BSN-DDC 基础网络关键知识点(五)跨链机制、官方 SDK 说明、开发资料汇总

    id BSN 2021 公众号 BSN研习社 2022年1月25日 区块链服务网络发展联盟 简称 BSN联盟 上线推出了 BSN DDC基础网络 并进入试商用阶段 同时 BSN DDC官网门户 ddc bsnbase com 上线发布 供D
  • C# 写入二进制文件

    试验1 using System using System IO using System Runtime Serialization Formatters Binary namespace 创建二进制文件 Serializable cla
  • 利用ChatGPT做市场营销的终极指南【建议收藏】

    ChatGPT是一种基于AI技术的语言模型 它可以与用户进行对话和交互 它被广泛应用于各个领域 包括市场营销 作为一名市场营销人员 您可以使用ChatGPT来获得创意 解决问题和生成内容 下面是190个ChatGPT提示 可帮助营销人员更好
  • C语言:数组的应用2——扫雷(递归实现地图变化)

    之前呢跟大家分享了二维数组实现的小游戏 三子棋 井字棋 大家都看懂了吗 今天给大家分享一下用数组实现的扫雷小游戏 先看看最终的效果吧 我设计的这个扫雷游戏 可以让玩家自己选择游戏难度 有简单 适中 困难三种模式 并利用递归的方式去改变地图
  • ThreadPoolExecutor类讲解

    一 ThreadPoolExecutor类讲解 1 线程池状态 五种状态 线程池 的状态 说明 RUNNING 允许提交并处理任务 SHUTDOWN 不允许提交新的任务 但是会处理完已提交的任务 STOP 不允许提交新的任务 也不会处理阻塞