java之juc之线程池

阻塞队列

队列写入元素的时候:如果队列满了,就必须阻塞等待
队列获取元素的时候:如果队列是空的,必须阻塞等待生产

在这里插入图片描述

由以上类结构图可以看出,BlockingQueue不是新的东西

使用阻塞队列的场景:多线程并发处理,线程池!

  • SynchronousQueue:是一个特殊的阻塞队列,它并不保存任何元素,每次插入操作必须等待另一个线程的移除操作,每次移除操作必须等待另一个线程的插入操作,因此它可以用于两个线程之间进行数据交换。

  • LinkedBlockingQueue:是一个无界(元素无上限,Integer.Max_Value)的阻塞队列,底层是由链表实现的,可以存储任意数量的元素。

  • ArrayBlockingQueue:是一个有界的阻塞队列,底层是由数组实现的,

  • DelayQueue:队列内元素必须实现Delayed接口,这就意味着你传进去的任务必须先实现Delayed接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务

  • PriorityBlockingQueue:是一个支持优先级的阻塞队列,底层是由堆实现的,可以根据元素的优先级顺序进行排序。当添加元素时,会根据元素的优先级自动排序,获取元素时会返回当前队列中优先级最高的元素。当队列为空时,获取元素的操作将会阻塞,直到队列中有元素可用。

ArrayBlockingQueue 阻塞队列

package com.demo.juc.bq;

import java.util.Collection;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;

public class TestBq {

    public static void main(String[] args) throws InterruptedException {
        // List Set Collection
        // BlockingQueue 不是新的东西

//        test1();
//          test2();
//        test3();
        test4();
    }


    /**
     * 队列满了还添加/队列空了还取出
     *
     * 抛出异常
     */
    public static void test1(){
        // 队列的大小
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);

        // 都是t
        System.out.println(arrayBlockingQueue.add("a"));
        System.out.println(arrayBlockingQueue.add("b"));
        System.out.println(arrayBlockingQueue.add("c"));

        // java.lang.IllegalStateException: Queue full 抛出异常!
        // System.out.println(arrayBlockingQueue.add("d"));

        // 先进先出 abc
        System.out.println(arrayBlockingQueue.remove());
        System.out.println(arrayBlockingQueue.remove());
        System.out.println(arrayBlockingQueue.remove());

        // java.util.NoSuchElementException
        // System.out.println(arrayBlockingQueue.remove());
    }


    /**
     * 队列满了还添加,会返回false
     *
     * 队列空了还取出,会返回null
     *
     * 两种都不抛出异常,有返回值
     */
    public static void test2(){
        ArrayBlockingQueue<Object> arrayBlockingQueue = new ArrayBlockingQueue<>(3);

        // t t t f
        System.out.println(arrayBlockingQueue.offer("a"));
        System.out.println(arrayBlockingQueue.offer("b"));
        System.out.println(arrayBlockingQueue.offer("c"));
        System.out.println(arrayBlockingQueue.offer("d"));

        // 查看队首元素是谁,a
        // 两个方法都是同样的作用
        System.out.println(arrayBlockingQueue.element());
        System.out.println(arrayBlockingQueue.peek());

        // a b c null
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
    }


    /**
     * 等待,阻塞(一直)
     */
    public static void test3() throws InterruptedException {
        ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);

        blockingQueue.put("a");
        blockingQueue.put("b");
        blockingQueue.put("c");

        // 队列没有位置了,就会一直阻塞
//        blockingQueue.put("d");

        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());

        // 没有这个元素,一直阻塞
        System.out.println(blockingQueue.take());

    }





    /**
     * 等待,阻塞(等待超时)
     */
    public static void test4() throws InterruptedException {
        ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);

        blockingQueue.offer("a");
        blockingQueue.offer("b");
        blockingQueue.offer("c");

        // 等待2s,如果还没有位置就超时退出
//        blockingQueue.offer("d",2, TimeUnit.SECONDS);


        blockingQueue.poll();
        blockingQueue.poll();
        blockingQueue.poll();
        // 等待2s,如果还没有元素可以取出就退出
        blockingQueue.poll(2,TimeUnit.SECONDS);

    }

}

SynchronousQueue 同步队列

没有容量,进去一个元素,必须等待取出来之后,才能再往里面放一个元素。

相当于该队列容量只有1。

package com.demo.juc.bq;

import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;


/**
 * 同步队列
 *
 * 和其他的BlockingQueue不一样,SynchronousQueue不存储元素
 * put了一个元素,必须先从里面take出来,否则不能再put进去值
 */
public class SynchronousQueueDemo {

    public static void main(String[] args) {
        SynchronousQueue<String> synchronousQueue = new SynchronousQueue<>(); // 同步队列

        new Thread(()->{

            try {
                System.out.println(Thread.currentThread().getName() + "put 1");
                synchronousQueue.put("1");
                System.out.println(Thread.currentThread().getName() + "put 2");
                synchronousQueue.put("2");
                System.out.println(Thread.currentThread().getName() + "put 3");
                synchronousQueue.put("3");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"t1").start();


        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName()+synchronousQueue.take());
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName()+synchronousQueue.take());
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName()+synchronousQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"t2").start();


        /**
         * t1put 1
         * t21
         * t1put 2
         * t22
         * t1put 3
         * t23
         *
         *
         * 如果是阻塞队列就是全放进去,然后再全取出来
         */

    }
}

线程池

线程池就是创建若干个可执行的线程放入一个池(容器)中,有任务需要处理时,会提交到线程池中的任务队列,处理完之后线程并不会被销毁,而是仍然在线程池中等待下一个任务。

线程池常问问题:三大方法、7大参数、4种拒绝策略

程序的运行,本质:占用系统的资源!优化资源的使用!

线程池、连接池、内存池、对象池。。。。。。创建和销毁十分浪费资源。

池化技术:开启和关闭线程是非常消耗资源的,我们事先准备好一些资源,有人要用,就来拿,用完之后再还回去。

线程池的好处:

  1. 降低资源的消耗
    通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗。

  2. 提高响应的速度
    重复利用线程池中线程,不需要每次都创建(使用线程池就能很好地避免频繁创建和销毁。)

  3. 方便管理
    线程对服务器来说是稀缺资源,如果无限制的去创建,肯定会导致系统的访问速度下降。

    可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

总结:线程复用、可以控制最大并发、管理线程。


这就类似于公司只要有一个新任务就招一个员工,最终会将公司的资源耗尽,这和我们的业务一样,最终分配给我们的堆空间或者栈空间,内存都是有限的,
如果在我们高并发系统里,比如这个业务非常大,要进行100W的异步任务查询,现在有100W个请求进来,假设一个请求就要开启10个异步任务,直接就会new 1000W个Thread,肯定会导致我们资源耗尽而最终的系统崩溃

我们以后在业务代码中,继承Thread类,实现Runnable接口,实现Callable接口,以上三种启动线程的方式都不用将所有的多线程异步任务都交给线程池执行

这就好比我们公司中50个员工,有任务的话就交给其中一个,如果都有活儿,就等处理完了再处理这个任务,这样做的好处就是我们达到了资源控制
我们只有这50个人,消耗的资源也就是这50个人的资源

线程池api

Java里面线程池的顶级接口是java.util.concurrent.Executor ,但是严格意义上讲 Executor 并不是一个线程池,而只是一个执行线程的工具。

真正的线程池接口是 java.util.concurrent.ExecutorService 。 要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在 java.util.concurrent.Executors 线程工厂类里面提供了一些静态工厂,生成一些常用的线程池。官方建议使用Executors(但阿里不推荐,听阿里的)工程类来创建线程池对象。 Executors类中有个创建线程池的方法如下:

// ExecurtorService:真正的线程池接口。常见子类ThreadPoolExecutors

// 执行线程任务,没返回值
void execute(Runnable command);

// 执行线程任务,有返回值
<T> Future<T> submit(Callable<T> task);

// 关闭连接池
void shutdown();


Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池

三大方法

在这里插入图片描述

package com.demo.juc.pool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

 // 用Executors或new ThreadPoolExecutor()创建线程池实际上都是实现ExecutorService

    // newFixedThreadPool 一个固定数量的线程池
    // 这个线程池不应该是每一个异步任务都创建一个线程池
    // 而是应该整个系统一个线程池,大家都把自己的任务交给这个池里执行

    // 当前系统中池只有一两个,可能有核心业务的或者非核心业务的
    // 每个异步任务,直接提交给线程池,让它自己去执行



// Executors 工具类、3大方法
// 使用了线程池之后,使用线程池来创建线程
public class Demo01 {

    public static void main(String[] args) {
//        ExecutorService threadPool = Executors.newSingleThreadExecutor();// 单个线程
//        ExecutorService threadPool = Executors.newFixedThreadPool(5); // 创建一个固定的线程池大小
        ExecutorService threadPool = Executors.newCachedThreadPool(); // 可伸缩的线程池


        try {
            for (int i = 0; i < 10; i++) {
            	// submit可以传入Runnable接口和Callable接口,可以获取到任务的返回值
        		// execute 可以只能传入Runnable接口,不能获取到任务的返回值
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"ok");
                });

            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }


        /**
         *
         * 以上三个不同的线程池操作线程的结果分别是
         *
         * pool-1-thread-1ok
         * pool-1-thread-1ok
         * pool-1-thread-1ok
         * pool-1-thread-1ok
         * pool-1-thread-1ok
         * pool-1-thread-1ok
         * pool-1-thread-1ok
         * pool-1-thread-1ok
         * pool-1-thread-1ok
         * pool-1-thread-1ok
         *
         * Process finished with exit code 0
         * 同一个线程
         *
         *
         *
         *
         *
         *
         *pool-1-thread-1ok
         * pool-1-thread-3ok
         * pool-1-thread-2ok
         * pool-1-thread-5ok
         * pool-1-thread-4ok
         * pool-1-thread-3ok
         * pool-1-thread-1ok
         * pool-1-thread-4ok
         * pool-1-thread-5ok
         * pool-1-thread-2ok
         *
         * Process finished with exit code 0
         *
         *
         *
         *
         *
         *
         * newCachedThreadPool()
         *
         * 如果同时执行10个任务,它创建的就有10个线程
         *
         * 测试如果是100个任务,它创建的是三十多个线程(这个数量没有逻辑)
         *
         *
         */
    }
}

源码分析

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



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




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



// 发现三大方法的本质是ThreadPoolExecutor()

public ThreadPoolExecutor(int corePoolSize, // 核心线程池大小【一直存在,除非设置allowCoreThreadTimeOut】
                          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;
}








/**  工作顺序:
         *
         *  1. 线程池创建,准备好 core 数量的核心线程,准备接受任务
         *  2. 新的任务进来,用 core 准备好的空闲线程执行。
         *  3. core 满了,就将再进来的任务放入阻塞队列中。空闲的 core 就会自己去阻塞队列获取任务执行
         *  4. 阻塞队列满了,就直接开新线程执行,最大只能开到 max 指定的数量
         *      max 都执行好了。Max-core 数量空闲的线程会在 keepAliveTime 指定的时间后自动销毁。
         *      最终保持到 core 大小
         *
         *      new LinkedBlockingQueue<>();默认是Integer的最大值。内存不够
         *  5. 如果线程数开到了 max 的数量,还有新任务进来,就会使用 reject 指定的拒绝策略进行处理
         *  6. 所有的线程创建都是由指定的 factory 创建的
         *
         *
         *  面试题
         *
         *  一个线程池,core 7,max 20,queue 50,100并发进来怎么分配的?
         *
         *      7个会立即得到执行,50个会进入队列,再开13个进行执行。剩下的30个就使用拒绝策略。
         */

七大参数和四种拒绝策略

package com.atlinxi.gulimall.springdemo.juc.pool;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


/**
拒绝策略,我觉得目的可能是避免发生拒绝的,如果出现了拒绝,那就是服务器承载不了那么多的线程并发,需要减少并发或者提升服务器的性能了,
所以它主要目的应该是临时处理和记录,以便后续的处理,一般来说肯定是不想抛弃该任务而做的补偿措施。

线程池工作中,如果任务量很大,超过系统实际承载能力时,如果不予理睬,接着可能系统就崩溃了,
所以jdk内置提供了线程池的4种拒绝策略,合理的解决这种问题。
*/

/**
 * ThreadPoolExecutor.CallerRunsPolicy() 哪来的去哪里
 * 		不启动线程池线程,直接调用run方法,这样的话实际上就是同步调用了。
 * 		它会让调用线程池的线程自己来运行任务。
 * 		当任务被拒绝时,由提交任务的线程来执行该任务。
 * 		第一,新提交的任务不会被抛弃,不会造成业务损失。

		第二,由于谁提交任务谁负责任务,提交任务的路线必须负责任务,
			执行任务需要时间,在此期间,提交任务的路线被占有,不提交新任务,任务提交速度变慢,相当于负面反馈。
			在此期间,线程池的线程也可以充分利用这个时间执行一部分任务,腾出一定的空间,相当于给线程池一定的缓冲期。
 * 
 * ThreadPoolExecutor.AbortPolicy() 超过最大线程直接报错;
 * 		丢弃任务并抛出RejectedExecutionException异常,默认策略
 * 
 * ThreadPoolExecutor.DiscardPolicy() 队列满了,丢掉任务,不会抛出异常
 * 
 * ThreadPoolExecutor.DiscardOldestPolicy() 当任务被拒绝时,丢弃队列中最老(最早)的一个任务,并尝试再次提交被拒绝的任务。
 * 
 * 同时jdk也为我们提供了RejectedExecutionHandler接口,可以根据实际应用场景去自定义策略

	实际开发中我们会使用自定义拒绝策略,因为在自定义拒绝策略灵活好控制,
		可以在自定义拒绝策略中发送一条通知给消息中心,让消息中心发送告警信息给开发者,
		这样可以实时的监控线程池的运行状况,并能及时发现和排查问题。
 */
public class PoolTest {

    public static void main(String[] args) {


        // 自定义线程池,工作中创建线程池只会用这种方式

		// 最大线程到底该如何定义
		// 1. cpu密集型 如果服务器是12核的,就能支持12条线程同时执行,
		//		几核就定义为几,可以保证cpu效率最高

		// 获取cpu的核数
		// 这就是cpu密集型的逻辑
		sout(Runtime.getRuntime().availableProcessors());

		// 2. io密集型。判断程序中十分耗io的线程,大于这个数即可,通常会设置两倍,
		// 除了这15个,还会剩下15个执行别的线程,就不会造成系统的阻塞
		// 程序中有15个大型任务,io十分占用资源
		

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy());// 最大线程数和队列都满了,但是还有人进来,不处理这个线程,并抛出异常


        try {
            // 最大承载:队列 + 最大线程数
            for (int i = 0; i < 9; i++) {
                threadPoolExecutor.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPoolExecutor.shutdown();
        }

        /**
         * 线程池的线程使用顺序
         *
         * 核心线程数 -》 队列 -》 最大线程数 -》 拒绝策略
         *
         * 6个任务以内2个线程执行
         * 6个任务3个线程执行
         * 7个任务4个线程执行
         * 8个任务5个线程执行
         * 9个任务由于超过了最大线程数,我们的拒绝策略又是AbortPolicy(),所以报java.util.concurrent.RejectedExecutionException
         *
         */
    }
}

自定义拒绝策略

dubbo
直接继承的 AbortPolicy

  • 输出了一条警告级别的日志,日志内容为线程池的详细设置参数,以及线程池当前的状态,还有当前拒绝任务的一些详细信息。可以说,这条日志,使用dubbo的有过生产运维经验的或多或少是见过的,这个日志简直就是日志打印的典范,其他的日志打印的典范还有spring。得益于这么详细的日志,可以很容易定位到问题所在
  • 输出当前线程堆栈详情,这个太有用了,当你通过上面的日志信息还不能定位问题时,案发现场的dump线程上下文信息就是你发现问题的救命稻草,这个可以参考
  • 继续抛出拒绝执行异常,使本次任务失败,这个继承了JDK默认拒绝策略的特性
// 通过实现RejectedExecutionHandler接口,自定义一个拒绝策略类,重写它的rejectedExecution()方法:
public class CustomRejectionHandler implements RejectedExecutionHandler {

    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        System.out.println(r.toString() + "被拒绝了,执行入库操作,之后手动补偿");
    }
}

如何判断线程池中的任务是否执行完成?

判断线程池任务执行是否完成,有以下几种方法:

//  isTerminated()
//  线程池提供了一个原生函数isTerminated()来判断线程池中的任务是否全部完成。
// 当线程池中所有任务都执行完成之后,线程池就会进入终止状态,此时调用isTerminated()方法返回的结果就是true
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);
// 提交任务
executor.execute(() -> {
    // 任务代码
});
// 关闭线程池
executor.shutdown();
// 判断任务是否完成
if (executor.isTerminated()) {
    System.out.println("所有任务已完成");





// getCompletedTaskCount():我们可以通过判断线程池中计划执行任务和已完成任务,
// 来判断线程池是否已经执行完成。如果相等就代表已经执行完成了
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);
// 提交任务
executor.execute(() -> {
    // 任务代码
});
// 判断任务是否完成
if (executor.getTaskCount() == executor.getCompletedTaskCount()) {
    System.out.println("所有任务已完成");
}




// FutureTask():当你提交一个Callable或者RRunnable任务到达线程池的时候,
// 你可以将它包装到一个FutureTask对象中。然后调用FutureTask的get()方法来获取任务的结果,
// 如果任务没有完成,get()方法将会阻塞,直到任务完成并且结果可用。所以,只要get()方法有返回结果,就直到任务是否完成
// 创建一个 Callable 任务
Callable<Integer> task = new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
        // 执行任务,返回结果
        return doSomeWork();
    }
};
 
// 创建一个 FutureTask 对象来包装任务
FutureTask<Integer> futureTask = new FutureTask<>(task);
 
// 提交任务到线程池
ExecutorService executor = Executors.newFixedThreadPool(1);
executor.execute(futureTask);
 
// 获取任务结果
try {
    Integer result = futureTask.get();  // 这里会阻塞,直到任务完成
    System.out.println("任务已完成,结果是:" + result);
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}

线程池的执行原理

Java线程池的执行原理主要包括以下几个步骤:

  1. 线程池创建:通过ThreadPoolExecutor类创建线程池,并设置核心线程数、最大线程数、队列容量、保持存活时间等参数。

  2. 任务提交:调用execute或submit方法提交任务。

  3. 任务执行:如果当前运行的线程数小于核心线程数,则创建一个新线程来执行任务;如果大于或等于核心线程数且任务队列未满,则将任务加入到任务队列中等待执行;如果队列也满了,并且当前运行的线程数小于最大线程数,则创建新的非核心线程来执行任务;如果达到最大线程数,则拒绝策略处理无法执行的任务。

  4. 任务执行结束:执行完毕的线程会回到线程池中等待下一个任务,而非核心线程在保持存活时间内会保持存活。

线程池非核心线程怎么设置暂停

在 Java 中,通常没有直接的方法来暂停线程池中的非核心线程。但是可以通过一些间接的方式来实现类似的效果。

一种方法是通过控制任务的提交来间接影响非核心线程的行为。比如,可以设置一个标志位,当需要暂停非核心线程时,停止向线程池提交新任务,这样非核心线程在完成当前任务后就会进入等待状态,因为没有新任务可执行。

以下是一个简单的示例代码:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ThreadPoolPauseExample {

    private static boolean pauseFlag = false;
    private static ExecutorService executorService = Executors.newFixedThreadPool(5);

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            executorService.execute(() -> {
                while (true) {
                    if (pauseFlag) {
                        try {
                            // 模拟暂停,等待恢复信号
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                        }
                    } else {
                        System.out.println(Thread.currentThread().getName() + " is working.");
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                        }
                    }
                }
            });
        }

        // 运行一段时间后暂停非核心线程
        TimeUnit.SECONDS.sleep(5);
        pauseFlag = true;

        // 恢复非核心线程
        TimeUnit.SECONDS.sleep(5);
        pauseFlag = false;

        // 关闭线程池
        executorService.shutdown();
        while (!executorService.isTerminated()) {
        }
        System.out.println("Shutdown complete.");
    }
}

在这个示例中,通过一个标志位pauseFlag来控制任务的执行。当pauseFlag为true时,任务中的线程进入等待状态。当需要恢复时,将标志位设置为false。

需要注意的是,这种方法并不是真正意义上的暂停非核心线程,只是通过控制任务的执行来达到类似的效果。并且在实际应用中,需要谨慎使用这种方法,确保不会引起死锁或其他并发问题。

第二,在人才成长过程中,我们要创造一个宽容的环境,让大家愿意畅所欲言,互相启发,把思想炸开。一是,喝咖啡是一种形式,网聊也是“喝咖啡”,比如2012实验室在群里的讨论就很激烈,关于软件突围方向在心声社区的回帖有1500多条,别看这一片骂声,这就是贵人指点、高僧开光、西汉张良在桥头获得的天书……。对新员工是多么好的指引。我为什么不愿意网聊,愿意喝咖啡呢?我认为讲话互动比文字互动效率高、表述更清楚。二是,高级专家要用更多精力来读文献,而不是埋头做事。比如,把一半时间用来读文献,写写感想。三是,我们“黄大年茶思屋”应该号召所有员工把他认为好的文献转帖过来,如果涉及知识产权问题,就把索引贴上来就行,大家再去那个网站上查阅,这也是一个沟通方式。

今天听了汇报,你们工作的基本思路是清晰的。历史滚滚向前就会有新陈代谢,有人选择离开就会有继任上来,但是我们要形成一个良好机制,让优秀人才涌现,英雄倍出。

任正非

  • 15
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值