java线程,线程池

线程状态

NEW 状态是指线程刚创建, 尚未启动

RUNNABLE 状态是线程正在正常运行中, 当然可能会有某种耗时计算/IO等待的操作/CPU时间片切换等,
这个状态下发生的等待一般是其他系统资源, 而不是锁, Sleep等

BLOCKED 这个状态下, 是在多个线程有同步操作的场景, 比如正在等待另一个线程的synchronized 块的执行释放,
或者可重入的 synchronized块里别人调用wait() 方法, 也就是这里是线程在等待进入临界区
WAITING 这个状态下是指线程拥有了某个锁之后, 调用了他的wait方法, 等待其他线程/锁拥有者调用 notify / notifyAll
一遍该线程可以继续下一步操作, 这里要区分 BLOCKED 和 WATING 的区别, 一个是在临界点外面等待进入,
一个是在理解点里面wait等待别人notify, 线程调用了join方法 join了另外的线程的时候, 也会进入WAITING状态,
等待被他join的线程执行结束

TIMED_WAITING 这个状态就是有限的(时间限制)的WAITING, 一般出现在调用wait(long),
join(long)等情况下, 另外一个线程sleep后, 也会进入TIMED_WAITING状态

TERMINATED 这个状态下表示 该线程的run方法已经执行完毕了, 基本上就等于死亡了(当时如果线程被持久持有, 可能不> 会被回收)

线程创建方式

继承Thread类

public class MyThread extends Thread {

	@Override
	public void run() {
		System.out.println("MyThread");
	}
	
	public static void main(String[] args) {
		new MyThread().start();
	}
}


实现runnable接口

public class MyRunnable implements Runnable {

	@Override
	public void run() {
		System.out.println("My Runnable");
	}
	
	public static void main(String[] args) {
		new Thread(new MyRunnable()).start();
	}

}

注意,下方这样会调用重写的Thread的run方法,而不是你实现接口的run方法,因为接口的run方法是在Thread的原始run方法中调用的

@Override
public void run() {
if (target != null) {
target.run();
}
}

public class MyRunnable implements Runnable {

	@Override
	public void run() {
		System.out.println("My Runnable");
	}
	
	public static void main(String[] args) {
		new Thread(new MyRunnable()){

			@Override
			public void run() {
				System.out.println("重写Thread的run方法");
			}
			
		}.start();;
	}
}

Callable和FutureTask(runnable无法返回数据,callable可以)

public class MyCallable implements Callable<Integer>{

	@Override
	public Integer call() throws Exception {
		//让线程停5秒
		Thread.sleep(5000);
		return 1;
	}
	
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		FutureTask<Integer> futureTask = new FutureTask<>(new MyCallable());
		new Thread(futureTask).start();
		//get方法会等待,等待线程返回
		System.out.println(futureTask.get());
	}
}

//判断任务实现结束,包括异常情况以及被取消,也会返回true
futureTask.isDone();
//取消这个任务,参数为如果任务以及开始运行了,是否去打断它
Attempts to cancel execution of this task. This attempt willfail if the task has already completed, has already been cancelled,or could not be cancelled for some other reason. If successful,and this task has not started when cancel is called,this task should never run. If the task has already started,then the mayInterruptIfRunning parameter determineswhether the thread executing this task should be interrupted inan attempt to stop the task. 

futureTask.cancel(true);

线程池

ThreadPoolExecutor
主要有一下这些参数

corePoolSize:核心池的大小,这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
maximumPoolSize:线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程;
keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
unit:参数keepAliveTime的时间单位,有7种取值
workQueue:一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响
threadFactory:一个用来创建线程的工厂
rejectedExecutionHandler:拒绝策略,1.直接抛弃,2.抛弃并抛出异常3.通过提交任务的线程执行4.删除队列头部任务,重新尝试执行


同时java提供了Executors工具类来方便的创建线程池

newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

public static void main(String[] args) {
		ExecutorService pool = Executors.newSingleThreadExecutor();
		pool.execute(new MyRunnable());
		//pool.shutdown();
	}

newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

public static void main(String[] args) {
		ExecutorService pool = Executors.newFixedThreadPool(5);
		for(int i = 0; i < 10; ++i){
			final int index = i;
			pool.execute(new Runnable() {
				
				@Override
				public void run() {
					System.out.println(index);
					try {
						Thread.sleep(5000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			});
		}
	}

newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。

public static void main(String[] args) {
		ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);
		pool.schedule(new Runnable() {
			
			@Override
			public void run() {
				System.out.println("线程延迟5秒执行");
			}
		}, 5, TimeUnit.SECONDS);
	}
public static void main(String[] args) {
		ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);
		pool.scheduleAtFixedRate(new Runnable() {
			
			@Override
			public void run() {
				System.out.println("从0秒开始,每隔2秒周期性执行");
			}
		}, 0, 2, TimeUnit.SECONDS);	
	}

scheduleAtFixedRate :这个是按照固定的时间来执行,简单来说:到点执行
scheduleWithFixedDelay:这个呢,是等上一个任务结束后,在等固定的时间,然后执行。简单来说:执行完上一个任务后再执行


这里写图片描述

execute和submit方法的区别

  1. execute只能接受runnable,submit可以接受callable
  2. execute没有返回值,submit有返回值
  3. execute异常会直接抛出,submit要get结果才能收到异常

阻塞队列BlockingQueue

https://www.cnblogs.com/WangHaiMing/p/8798709.html

public interface BlockingQueue<E> extends Queue<E> {

    //将给定元素设置到队列中,如果设置成功返回true, 否则抛出异常。如果是往限定了长度的队列中设置值,推荐使用offer()方法。
    boolean add(E e);

    //将给定的元素设置到队列中,如果设置成功返回true, 否则返回false. e的值不能为空,否则抛出空指针异常。
    boolean offer(E e);

    //将元素设置到队列中,如果队列中没有多余的空间,该方法会一直阻塞,直到队列中有多余的空间。
    void put(E e) throws InterruptedException;

    //将给定元素在给定的时间内设置到队列中,如果设置成功返回true, 否则返回false.
    boolean offer(E e, long timeout, TimeUnit unit)
        throws InterruptedException;

    //从队列中获取值,如果队列中没有值,线程会一直阻塞,直到队列中有值,并且该方法取得了该值。
    E take() throws InterruptedException;

    //在给定的时间里,从队列中获取值,如果没有取到会抛出异常。
    E poll(long timeout, TimeUnit unit)
        throws InterruptedException;

    //获取队列中剩余的空间。
    int remainingCapacity();

    //从队列中移除指定的值。
    boolean remove(Object o);

    //判断队列中是否拥有该值。
    public boolean contains(Object o);

    //将队列中值,全部移除,并发设置到给定的集合中。
    int drainTo(Collection<? super E> c);

    //指定最多数量限制将队列中值,全部移除,并发设置到给定的集合中。
    int drainTo(Collection<? super E> c, int maxElements);
}
线程池队列
SingleThreadExecutorLinkedBlockingQueue
FixedThreadPoolLinkedBlockingQueue
CachedThreadPoolSynchronousQueue
ScheduledThreadPoolDelayedWorkQueue

CachedThreadPool为什么使用SynchronousQueue
由于ThreadPoolExecutor内部实现任务提交的时候调用的是工作队列(BlockingQueue接口的实现类)的非阻塞式入队列方法(offer方法),因此,在使用SynchronousQueue作为工作队列的前提下,客户端代码向线程池提交任务时,而线程池中又没有空闲的线程能够从SynchronousQueue队列实例中取一个任务,那么相应的offer方法调用就会失败(即任务没有被存入工作队列)。此时,ThreadPoolExecutor会新建一个新的工作者线程用于对这个入队列失败的任务进行处理(假设此时线程池的大小还未达到其最大线程池大小)。
使用SynchronousQueue的目的就是保证“对于提交的任务,如果有空闲线程,则使用空闲线程来处理;否则新建一个线程来处理任务,也就符合了之前的上面那个流程图。

因为他不存储元素,需要取和存要对应,否则阻塞,所以当没有空闲线程的时候,提交任务(非阻塞提交,失败就直接返回),无法放入队列,就会创建新的线程

Executors.new ThreadPoolExecutor()里核心线程数为0,所以你提交的任务总会尝试去排队,可以进行重用,如果自己创建,把核心线程数改为10,那么前10个任务会直接创建,超过10的会进行重用


/**
 * @program: java8
 * @description:
 * @author: dengbin
 * @create: 2018-12-04 15:12
 **/

public class MyRunnable implements Runnable {

    @Override
    public void run() {
        try {
            Thread.sleep(1000);
            System.out.print(Thread.currentThread().getName() + " ");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


/**
 * @program: java8
 * @description:
 * @author: dengbin
 * @create: 2018-12-04 15:13
 **/

public class Main {
    public static void main(String[] args) throws InterruptedException {
    	//自己创建,核心线程数为10
    	//可以看出,前10个线程直接创建,后面会重用
    	//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-7 pool-1-thread-8 
    	//pool-1-thread-9 pool-1-thread-10 
    	//pool-1-thread-9 pool-1-thread-10 
    	//pool-1-thread-9 pool-1-thread-10 
    	//pool-1-thread-9 pool-1-thread-10 
    	//pool-1-thread-9 pool-1-thread-10 
    	//pool-1-thread-9 pool-1-thread-10 

        ExecutorService executorService =  new ThreadPoolExecutor(10, Integer.MAX_VALUE,
                60L, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>());;
        MyRunnable myRunnable = new MyRunnable();
        for (int i = 0; i < 20; ++i) {
            executorService.submit(myRunnable);
            Thread.sleep(1000);
        }
        executorService.shutdown();
    }
}

//若把线程池改为 ExecutorService executorService =  Executors.newCachedThreadPool();
//核心线程数为0
//只创建了2个线程,一直在复用
//pool-1-thread-1 pool-1-thread-2 
//pool-1-thread-1 pool-1-thread-2 
//pool-1-thread-1 pool-1-thread-2 
//pool-1-thread-1 pool-1-thread-2 
//pool-1-thread-1 pool-1-thread-2 
//pool-1-thread-1 pool-1-thread-2 
//pool-1-thread-1 pool-1-thread-2 
//pool-1-thread-1 pool-1-thread-2 
//pool-1-thread-1 pool-1-thread-2 
//pool-1-thread-1 pool-1-thread-2

参考
https://blog.csdn.net/cpf2016/article/details/50150205
https://www.cnblogs.com/goody9807/p/6515128.html
https://www.cnblogs.com/ElEGenT/p/6392465.html
https://www.cnblogs.com/baby-bear/p/4058534.html
https://www.cnblogs.com/WangHaiMing/p/8798709.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值