本贴最后更新于 211 天前,其中的信息可能已经时移世易

new Thread() 方式

执行一个异步任务,我们可以这样做

new Thread(new Runnable() {
    
	@Override
	public void run() {
	    System.out.println("ThreadName:"+Thread.currentThread().getName());
	}
}).start();

以上方式是可以实现的,但是存在诸多弊端:
1. 每次都重新 new Thread 新建对象耗费性能;
2. 缺乏统一管理,并发情况下可能无休止的创建线程,随时可能 oom;
3. 无法灵活执行线程,如定时、公平、中断等;
4. 针对耗时较短的异步行为,浪费资源及时间(如下图)
Thread生命周期
以上我们可以看到,真正的运行状态其实只占整个 Thread 生命周期的一小部分,如果只是为了执行一个耗时或占用资源较小的操作,可能开启线程的消耗比线程工作的周期还要长。
相比 new Thread,Java 提供了四种线程池可用
* Executors.newCachedThreadPool(); // 可伸缩线程池
* Executors.newFixedThreadPool(5); // 固定数量线程池
* Executors.newSingleThreadExecutor(); // 单一线程池
* Executors.newScheduledThreadPool(5); // 可定时执行线程池
其实还有一种 single 和 schedule 的结合体
* Executors.newSingleThreadScheduledExecutor();

  1. newCachedThreadPool
    创建一个可伸缩的缓存线程,如果线程池长度超过需要处理的长度,则收回部分线程,如果没有可收回的,就创建新线程,代码如下
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
	  final int index = i;

	cachedThreadPool.execute(new Runnable() {
		  @Override
		  public void run() {

			  try {
				  Thread.sleep(index * 1000);
				  System.out.println(Thread.currentThread().getName());
			  } catch (InterruptedException e) {
				  e.printStackTrace();
			  }
		  }
	  });
}
cachedThreadPool.shutdown();

结果如下, 当需要执行第 7 个的时候,重复使用了线程 1

pool-1-thread-1
pool-1-thread-2
pool-1-thread-3
pool-1-thread-4
pool-1-thread-5
pool-1-thread-6
pool-1-thread-1
pool-1-thread-7
pool-1-thread-8
pool-1-thread-9
  1. newFixedThreadPool
    创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。示例代码如下:
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(4);
for (int i = 0; i < 20; i++) {
	  final int index = i;
	  fixedThreadPool.execute(new Runnable() {

		  @Override
			  public void run() {
				  try {
					  System.out.println(Thread.currentThread().getName());
					  Thread.sleep(2000);
				  } catch (InterruptedException e) {
					  // TODO Auto-generated catch block
					  e.printStackTrace();
				  }
			  }
	  });
}
fixedThreadPool.shutdown();

因为线程池大小为 4 个,每个任务输出后等待 2 秒,所以每次会打印 4 个线程名字。
定长线程池大小最好是根据业务需求和机器资源配置进行设置,选择一个合适的数字,而不是随意想写多少就写多少。

  1. newScheduledThreadPool

    创建一个定长线程池,支持定时延期及周期性任务执行。代码如下:
    延期 3S 执行

    ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
    scheduledThreadPool.schedule(new Runnable() {
    
    	@Override
    	public void run() {
    		System.out.println("delay 3 seconds");
    	}
    }, 3, TimeUnit.SECONDS);
    

    首次延期 1S,然后每 3S 执行一次,倒计时 3 次之后,则退出定时器

    CountDownLatch countDownLatch = new CountDownLatch(3);
    ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
    scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
    
    	@Override
    	public void run() {
    		System.out.println("delay 3 seconds");
    		countDownLatch.countDown();
    	}
    }, 1,3, TimeUnit.SECONDS);
    try {
    	System.out.println("开始等待倒计时...");
    	countDownLatch.await();
    } catch (InterruptedException e) {
    	e.printStackTrace();
    }
    System.out.println("倒计时结束");
    if (countDownLatch.getCount() == 0L) {
    	scheduledThreadPool.shutdown();
    }
    
  2. newSingleThreadExecutor
    创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务
    ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
    for (int i = 0; i < 10; i++) {
    	singleThreadExecutor.execute(new Runnable() {
    
    		@Override
    		public void run() {
    			try {
    				System.out.println(Thread.currentThread().getName());
    				Thread.sleep(2000);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    	});
    }
    

    会按照顺序依次执行。

  • 线程
    69 引用 • 83 回帖
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    2107 引用 • 6995 回帖 • 1081 关注
感谢    关注    收藏    赞同    反对    举报    分享