java面试总结(4)之多线程

1.什么是线程 

线程是操作系统运行调度的最小单元,是进程的元素,进程是由多个线程组合而来的。

每条线程有自己的栈内存,一个进程内的所有线程共享一片堆内存

多线程可以对运算密集型任务提速

2.简单描述一下线程安全

在多线程环境下运行的代码,如果存在多个线程资源共享就存在线程安全的问题,例如同时操作一个成员变量

3.Thread 类中的start() 和 run() 方法有什么区别

start是启动线程的方法,run方法内部会调用目标对象的run方法,目标对象的run方法是线程运行的载体

直接调用run方法也会运行,只是在当前线程中同步运行,并没有新的线程启动

4.Java多线程中调用wait() 和 sleep()方法有什么不同

sleep方法是Thread类提供的,wait方法是Object提供的

sleep只能等到休眠时间到,自己醒来,wait可以自己醒来,也可以被唤醒

sleep不会释放锁,wait会释放锁

wait必须在synchronized环境内使用,因为会释放锁,没有synchronized何来锁

5.notify和notifyAll的区别

当一个线程wait后,只有通过notify或notifyAll才能被唤醒,也就是重新获得了锁竞争的资格

notify只能唤醒某个线程,notifyAll可以唤醒所有的线程(所以如果多个线程被阻塞,须使用该方法)

注意获得竞争锁的资格不是获得了锁

6.为什么wait, notify 和 notifyAll这些方法不在thread类里面

这三个方法都是锁级别的操作,比如调用wait的时候需要去释放对象的锁,如果定义在thread里面,当前线程并不知道要释放的是哪个对象的锁,因此定义在Object里面,由被锁的对象去调用更为合理。

同理notify和notifyAll这两个方式是去唤醒被锁对象的阻塞线程,如果定义在thread里面,那么当前线程还是不得知需要唤醒的是哪个被锁对象的阻塞线程,因此也定义在Object里面

7.为什么wait和notify方法要在同步块中调用

如果在同步块外调用会抛异常:java.lang.IllegalMonitorStateException: current thread not owner

8.Thread类中的yield方法有什么作用

可以暂停当前线程,让其他线程获取更高的CPU执行优先级,但不保证当前线程一定后执行

9.Java线程池中submit() 和 execute()方法有什么区别

都可以提交线程,只是返回不同,submit定义在ExecutorService中,可以返回一个线程计算结果对象Future<V>,execute定义在Executor中,无返回值

10.如何强制启动一个线程

java中没有方法可以强制启动线程

11.interrupte、interrupted 和 isInterrupted的区别

interrupte是Thread的成员方法,中断线程

interrupted是Thread的静态方法,如果当前线程已被中断,则取消中断状态

isInterrupted是Thread的成员方法,判断当前线程是否中断

12.怎么检测一个线程是否拥有锁

Thread类提供了holdsLock(Object o)方法可以检测到o对象是否持有当前线程的锁,true表示有锁

13.如何让线程按既定顺序执行,线程的优先级

t1,t2,t3,先启动t3,t3中启动t2,t2中启动t1,执行顺序t1>t2>t3

setPriority(int newPriority) 调用当前线程的该方法可以设置线程执行顺序优先级,但不保证一定先执行,只是概率高了一些

14.什么是竞态条件.

多个线程同时竞争一个共享资源,并且对该资源进行更新操作(例如线程A 对2 +1,线程B对变量2+1,那么想要的结果应该是4,但是其实是3),那么各个线程会发生覆盖的现象,称为竞态条件

避免竞态条件的发生,可以对资源加同步锁

15.什么是ThreadLocal变量

ThreadLocal变量是本地线程的变量,是一种对共享变量的副本的拷贝,可以实现各个线程操作共享变量的副本,即共享变量变成了当前线程的私有属性,各个线程间的更新操作互不影响

与synchronized的区别:同步是指多个线程再操作共享资源的时候,同步进行,但操作的还是共享资源,而ThreadLocal则是对共享资源的一份拷贝,多个线程各自操作各自的拷贝,互不干扰

使用示例:

*ThreadLocal变量使用示例

//实体类
public class Person {

	private String name;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	@Override
	public String toString() {
		return "Person [name=" + name + "]";
	}
	
}

//使用ThreadLocal
public class Task1 implements Runnable{
	
	private ThreadLocal<Person> threadLocal = new ThreadLocal<Person>() {
		protected Person initialValue() {
			return new Person();
		}
	};

	@Override
	public void run() {
		System.out.println("ThreadName:" + Thread.currentThread().getName() + "   " + threadLocal.get());
		threadLocal.get().setName("t");
		System.out.println("ThreadName:" + Thread.currentThread().getName() + "   " + threadLocal.get());
	}
	
}

//未使用ThreadLocal
public class Task2 implements Runnable{
	
	private Person person = new Person();

	@Override
	public void run() {
		System.out.println("ThreadName:" + Thread.currentThread().getName() + "   " + person);
		person.setName("t");
		System.out.println("ThreadName:" + Thread.currentThread().getName() + "   " + person);
	}
	
}

//test
public class Test {

	public static void main(String[] args) {	
		//使用ThreadLocal
		test1();
		
		//未使用ThreadLocal
		//test2();
	}
	
	public static void test1() {
		Task1 task1 = new Task1();
		for(int i = 0; i < 5; i++) {
			new Thread(task1).start();
		}
	}
	
	public static void test2() {
		Task2 task2 = new Task2();
		for(int i = 0; i < 5; i++) {
			new Thread(task2).start();
		}
	}
}

打印结果:

使用threadlocal,test1():

ThreadName:Thread-1   Person [name=null]
ThreadName:Thread-0   Person [name=null]
ThreadName:Thread-0   Person [name=t]
ThreadName:Thread-3   Person [name=null]
ThreadName:Thread-3   Person [name=t]
ThreadName:Thread-4   Person [name=null]
ThreadName:Thread-4   Person [name=t]
ThreadName:Thread-2   Person [name=null]
ThreadName:Thread-1   Person [name=t]
ThreadName:Thread-2   Person [name=t]

未使用threadlcoal,test2():

ThreadName:Thread-0   Person [name=null]
ThreadName:Thread-0   Person [name=t]
ThreadName:Thread-1   Person [name=t]
ThreadName:Thread-1   Person [name=t]
ThreadName:Thread-2   Person [name=t]
ThreadName:Thread-2   Person [name=t]
ThreadName:Thread-3   Person [name=t]
ThreadName:Thread-3   Person [name=t]
ThreadName:Thread-4   Person [name=t]
ThreadName:Thread-4   Person [name=t]

可以看出使用threadlocal,各个线程独立操作person,未使用threadlocal则相互干扰

*加同步锁的示例

package cn.qu.td;

public class MyThread{
	
	static class Person {
		private Long id;
		private String name;
		public Long getId() {
			return id;
		}
		public void setId(Long id) {
			this.id = id;
		}
		public String getName() {
			return name;
		}
		public void setName(String name) {
			this.name = name;
		}
		
		
	}
	
	private static Person person = new Person();
	
	
	

	public static void main(String[] args) {
		
		Thread t1 = new Thread("t1"){
			public void run() {
				synchronized (person) {					
					person.setId(10L);
					person.setName("Lucy");
					System.out.println("t1.id:" + person.getId() + " t1.name:" + person.getName());
				}
			}
		};
		
		Thread t2 = new Thread("t2"){
			public void run() {
				synchronized (person) {					
					person.setId(20L);
					person.setName("Mary");
					System.out.println("t2.id:" + person.getId() + " t2.name:" + person.getName());
				}
			}
		};
		
		t1.start();
		t2.start();
		
	}
	
}

 

上述代码:控制台打印:

t1.id:10 t1.name:Lucy
t2.id:20 t2.name:Mary

因为加锁的缘故,两个线程并未相互干扰

*ThreadLocal一个典型的应用场景就是Hibernate中的session

16.描述一下volatile变量

保证内存可见性,但不保证原子性。

大致意思就是:一个写操作可以被并发读取到,但是没有锁的那种机制保证多个写的原子性。

应用场景:比较底层,通常用在解决变量重排序问题上

17.如何停止一个线程,join的用途

java中不能强制停止一个线程,只能等待其执行完毕后,自行停止

join的字面意思加入的意思,然而我们都知道他在java中的作用是阻塞主线程等待子线程执行结束,那么怎么理解加入这个意义呢

当子线程执行完毕后汇入到主线程,这下可以理解了join的意思啦,详情参考:join详解

例如:

package cn.qu.td;

public class MyThread{

	private volatile static Integer a = 10;

	public static void main(String[] args) {
		
		Thread t1 = new Thread("t1"){
			public void run() {
				for(int i = 1; i <= 10; i ++){
					a++;
					System.out.println(Thread.currentThread().getName() + ": a = " + a);
				}
			}
		};
		
		Thread t2 = new Thread("t2"){
			public void run() {
				for(int i = 1; i <= 10; i ++){
					a++;
					System.out.println(Thread.currentThread().getName() + ": a = " + a);
				}
			}
		};
		
		t1.start();
		t2.start();
		
		try {
			t1.join();
			t2.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		System.out.println(a);
		
	}
	
}

可以看到控制台最后一行打印出的a是30,就是等待t1,t2两个线程结束后才执行的最后一行打印a的代码

18.同步块内的线程抛出异常会发生什么

会释放锁,交由其他线程来使用资源

19.多线程编程都注意些什么

*给线程起个有意义且规范的名称

*加同步的时候尽量细粒度,例如能用同步代码块,就不要用同步方法

*合理的使用线程池来管理线程,而不是必须用线程池

*能用jdk提供的线程安全的API就不要用非线程安全的类,然后自己加同步,例如HashMap和ConcurrentHashMap

20.实现线程的几种方式以及区别

继承Thread类方式:

public class MyThread extends Thread {
	
	private Integer ticket = 10;
	
	@Override
	public void run() {
		while (true){
			synchronized (this) {
				if(ticket < 1) break;
				ticket --;
				System.out.println(Thread.currentThread().getName() + "卖出一张票,剩余" + ticket);
			}				
		}	
		
	}
	
	public static void main(String[] args) {
		new MyThread().start();
		new MyThread().start();
		new MyThread().start();
	}
	
}

实现Runnable接口方式:

public class MyThread implements Runnable {
	
	private Integer ticket = 10;
	
	public void run() {
		while (true){
			synchronized (this) {
				if(ticket < 1) break;
				ticket --;
				System.out.println(Thread.currentThread().getName() + "卖出一张票,剩余" + ticket);
			}				
		}		
	}
	
	public static void main(String[] args) {
		MyThread t = new MyThread2();		
		new Thread(t,"t1").start();
		new Thread(t,"t2").start();
		new Thread(t,"t3").start();
	}
	
}

实现Callable<V>接口方式:

public class MyThread implements Callable<MyThread> {
	
	protected Integer ticket = 10;
	
	public MyThread call() throws Exception {
		while (true) {
			synchronized (this) {
				if(ticket < 1) break;	
				ticket --;
				System.out.println(Thread.currentThread().getName() + "卖出一张票,剩余" + ticket);
                                //模拟线程1进来操作则抛出异常信息
				if("t1".equals(Thread.currentThread().getName()))throw new RuntimeException(Thread.currentThread().getName() + ".errortest");    
			}
		}
		
                //返回当前类的引用
		return this;
	}
	
	
	public static void main(String[] args) {

		MyThread t = new MyThread();		
		
                //创建一个线程池,并在线程池内部循环设置线程名称
		ExecutorService executorService = Executors.newCachedThreadPool(new ThreadFactory() {
			
                        //定义一个随方法调用的自增变量
			private AtomicInteger i = new AtomicInteger(1);
			
			public Thread newThread(Runnable r) {
				Thread thread = new Thread(r);
                                //设置线程名称
				thread.setName("t" + i.getAndIncrement());
				return thread;
			}
		});
		
                //声明一个线程执行完成后返回结果的存储集合
		List<Future<MyThread>> list = new ArrayList<Future<MyThread>>();

		for(int i = 0; i < 3; i ++){
                        //调用submit(Callable<V> c)启动线程
			Future<MyThread> future = executorService.submit(t);
                        //把线程运行的返回结果保存到集合中
			list.add(future);			
		}
		
                //线程运行完毕后,关闭阻塞
		executorService.shutdown();
		
		String error = "";
		
                //迭代线程运行的返回结果
		for (Future<MyThread> future : list) {
			try{			
				System.out.println("剩余票数:" + future.get().ticket);							
			}catch (Exception e) {
                                //如果某个线程抛出了异常则赋值给error
				error = e.getMessage();
			}
		}
		
                //打印出错线程的异常信息
		System.out.println(error);
		
	}
	
}

*Thread和Runnable的区别:

通过上述代码我们可以看到,使用Thread方式不方便线程间资源共享,当然了你可以把一个实现线程的Thread子类在包装给Thread创建的线程对象,这样便可以资源共享了,但这种方式很显然是为了支持Runnable创建线程而提供的构造,毕竟继承了Thread,就可以直接调用Thread的非私有属性和方法了,要是使用Thread包装一个子类,就显得多余,没意义了

Thread和Runnable的区别总结:

1,java不支持多继承,但支持多实现,因此使用Runnable方式比Thread方式更容易扩展

2,使用Runnable可以达到线程间资源共享的目的

*Runnable和Callable的区别:

两者都是接口,便于扩展,Callable的装饰类FutureTask也实现了Runnable,因此都支持线程间资源共享

Runnable是jdk1.0就有,Callable是jdk1.5才有的

Callable有返回值,返回值的类型既Callable的泛型类型,可以自定义返回值类型很方便,Runnable没有返回值

Callable可以把异常抛出去,并被主线程 在获取抛出异常线程的返回值时捕获到,Runnable抛出的异常,主线程无法捕获到

21.谈谈死锁

*死锁概述

多个线程间并发操作同一资源时,产生的相互等待对方的现象

*死锁产生的四个必要条件

互斥条件:一个资源只能同时被一个线程访问。即有同步

请求与保持条件:线程(因需要其他资源)出现等待,且未释放锁. 例如: sleep

不可抢占资源条件:当前线程需要等待的资源,被其他线程持有而且锁定可以理解为不可抢占,即需要等待的资源被加锁

循环等待条件:A线程锁定a资源需要b资源,B线程锁定b资源需要a资源,AB两个线程同时启动,就产生循环等待条件

*一个简单死锁示例

package cn.qu.td;

public class MyThread {

	final static Object o1 = new Object(), o2 = new Object();

	/*
	 * o1和o2中都加了同步,因此就     满足互斥条件:一个资源只能同时被一个线程占用
	 * Thread.sleep(500);  	满足请求与保持条件(阻塞且未释放锁,阻塞的目的通常是为了等待其他线程释放资源)
	 * t2占用o2资源后,t1中也需要o2资源,但是因为t2对o2加了锁,因此就   满足不可抢占资源条件
	 * t1对o1加锁>阻塞>t2对o2加锁>阻塞>t1获取不到o2等待>t2又获取不到t1也等待     于是满足循环等待条件
	 */
	public static void main(String[] args) {
	
		new Thread(new Runnable() {
			
			public void run() {
				synchronized (o1) {
					System.out.println(o1);
					try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					synchronized (o2) {
						System.out.println(o2);
					}
				}			
			}
		}, "t1").start();
		
		new Thread(new Runnable() {
			
			public void run() {
				synchronized (o2) {
					System.out.println(o2);
					try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					synchronized (o1) {
						System.out.println(o1);
					}
				}			
			}
		}, "t2").start();
		
	}

}

 

23.写一个简单的生产者、消费者示例

//商品类
public class Good{

	private String name;
	private Integer goodNumber;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Integer getGoodNumber() {
		return goodNumber;
	}
	public void setGoodNumber(Integer goodNumber) {
		this.goodNumber = goodNumber;
	}
	@Override
	public String toString() {
		return "Goods [name=" + name + ", goodNumber=" + goodNumber + "]";
	}
	
	//主函数中测试生产者消费者模型
	public static void main(String[] args) {
		List<Good> goods = new ArrayList<Good>();
		new Thread(new Producer(goods),"生产者").start();
		new Thread(new Consumer(goods),"消费者").start();
	}

}


//生产者
public class Producer implements Runnable {

	//商品容器
	private List<Good> goods;

	public Producer(List<Good> goods) {
		this.goods = goods;
	}
	
	//已生产的商品数量
	private Integer i = 0;
	
	public void run() {
		while(true){
			//休眠的目的是为了模拟动态演示生产和消费的效果
			try {
				Thread.sleep(500);
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}
			
			synchronized (goods) {
				//每次进来先唤醒其他的线程
				goods.notifyAll();
				//打印容器中商品的数量
				System.out.println(Thread.currentThread().getName() + "-" + "商品剩余数量:" + goods.size());
				
				if (goods.isEmpty()) {	//如果为空,则开始生产商品
					Good good = new Good();
					good.setName(UUID.randomUUID().toString());
					//把生产的商品添加到容器
					goods.add(good);
					
					//已生产商品数量加1
					i++;
					//打印当前生产的商品名称
					System.out.println(Thread.currentThread().getName() + "-" + "生产者生又产了一个商品:" + good);
					//打印生产者已经生产了多少个商品的数量
					System.out.println(Thread.currentThread().getName() + "-" + "生产者已生产的商品数量:" + i);
					
				} else {	//如果不为空,则阻塞当前线程
					try {
						goods.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}

}


//消费者
public class Consumer implements Runnable {

	//商品容器
	private List<Good> goods;

	public Consumer(List<Good> goods) {
		this.goods = goods;
	}
	
	//已消费的商品数量
	private Integer i = 0;

	public void run() {
		while (true) {
			//休眠的目的是为了模拟动态演示生产和消费的效果
			try {
				Thread.sleep(500);
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}
			
			synchronized (goods) {
				//每次进来先唤醒其他的线程
				goods.notifyAll();
				//打印容器中商品的数量
				System.out.println(Thread.currentThread().getName() + "-" + "商品剩余数量:" + goods.size());
				
				if (goods.isEmpty()) {	//如果为空,则阻塞当前线程
					try {
						goods.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}					
				} else {	//否则开始消费商品
					Good good = goods.get(0);
					goods.remove(good);
					
					//已消费商品的数量加1
					i++;
					//打印当前消费的商品名称
					System.out.println(Thread.currentThread().getName() + "-" + "消费者又消费了一个商品:" + good);
					//打印已消费的商品数量
					System.out.println(Thread.currentThread().getName() + "-" + "已消费商品的数量:" + i);					
				}
			}
		}
	}

}

24.什么是线程池

*系统频繁的创建线程和销毁线程对CPU的开销是比较大的,为了让系统减少创建和销毁线程的操作,可以构建一个容器来存放已创建好的线程,供新的任务来使用,可以把这个容器理解为线程池

*使用线程池的目的是为了减少频繁创建销毁线程所带来的开销

*JDK1.5提供了ExecutorService接口来作为线程池的规范继承自顶级接口Executor,以及它的最佳实现ThreadPoolExecutor类

*ThreadPoolExecutor的构造器接收的参数:

int corePoolSize  线程池中常驻线程数量

int maximumPoolSize 线程池中最大线程数量(=常驻线程+非常驻线程)

long keepAliveTime 线程池中非常驻线程的保留时间

TimeUnit unit 线程池中非常驻线程保留时间的单位:纳秒、微秒、毫秒、秒、分钟、小时、天

BlockingQueue<Runnable> workQueue 线程池中承载任务的队列,当任务被线程执行,则从队列中移除任务

ThreadFactory threadFactory 创建线程池中所使用线程的工厂

RejectedExecutionHandler handler 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理器(当然需要自己编写)

*ThreadPoolExecutor还提供了两个方法用于扩展每个task执行前后的逻辑

void beforeExecute(Thread t, Runnable r);  //线程t start之前执行

void afterExecute(Runnable r, Throwable t);  //任务r执行完成后执行,并且可以捕获到r执行异常

*ThreadPoolExecutor 的原理:

维护一个线程集合HashSet<Worker> workers   用于存放空闲线程,和大于空闲线程数且小于最大线程数的线程

维护一个任务队列LinkedBlockingQueue workQueue  用于execute存放任务

workers中的每个worker就是一个线程,当worker被启动后,会一直循环从workQueue中拿去task,一旦拿到,就调用task的run方法执行,参考runWorker(Worker w)方法就是这个逻辑

    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }

    private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?

        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }

            int wc = workerCountOf(c);

            // Are workers subject to culling?
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

每提交一个task,首先会判断线程数是否小于corePoolSize,

       如果是则新建一个常驻线程执行task,

       否则添加task到workQueue,workQueue满,则判断线程数是否小于maximumPoolSize,

               如果是新建非常驻线程,执行task,

               否则走拒绝策略。

*JDK1.5还提供了一个构建线程池的工具类Executors,通常我们使用这个工具类来构建一个线程池

*4种常用的线程池(都是ThreadPoolExecutor的对象,只是构建的过程不一样)

 

newSingleThreadExecutor(应用在多个有序执行场景)

通过Executors.newSingleThreadExecutor()方法可以构建一个单线程的线程池,即线程池始终只有一个线程,所有任务都会由该线程按照队列的方式执行,即先execute(Runnable r)则先执行

 

newFixedThreadPool(应用在持续低并发场景中)

通过Executors.newSingleThreadExecutor(int maximumPoolSize)或者Executors.newSingleThreadExecutor(int maximumPoolSize,ThreadFactory threadFactory)构建一个固定保留线程数量的线程池,当线程池中的数量达到maximumPoolSize后,则不再创建新的线程,而是使用空闲线程,如果无空闲线程则阻塞直到有空闲线程

 

newCachedThreadPool(缓存线程池使用最广泛)

*通过Executors.newCachedThreadPool()可以构建一个缓存线程池

*缓存线程池的原理:

当有新的任务时,线程池会查看有无空闲60秒内的线程,如果有则用该线程执行任务,如果没有,则创建新的线程来执行任务,执行完任务空闲后会把该线程缓存到线程池中,缓存的有效时间是60秒,当又有新的任务进来时,会去查看一下,刚才缓存的那个线程是否超过有效时间60秒,如果未超时,则用他来执行任务,否则把他销毁掉,创建一个新的线程来执行任务......

*使用示例以及测试原理:

public static void main(String[] args) throws InterruptedException {
		
	ExecutorService executor = Executors.newCachedThreadPool();

	Runnable r1 = new Runnable() {
		
		public void run() {
			
			System.out.println("执行r1任务的线程名称是:" + Thread.currentThread().getName());
		}
	};
	
	Runnable r2 = new Runnable() {
		
		public void run() {
			
			System.out.println("执行r2任务的线程名称是:" + Thread.currentThread().getName());
		}
	};
	
	Runnable r3 = new Runnable() {
		
		public void run() {
			
			System.out.println("执行r3任务的线程名称是:" + Thread.currentThread().getName());
		}
	};
	
	Runnable r4 = new Runnable() {
		
		public void run() {
			
			System.out.println("执行r4任务的线程名称是:" + Thread.currentThread().getName());
		}
	};
	
	Runnable r5 = new Runnable() {
		
		public void run() {
			
			System.out.println("执行r5任务的线程名称是:" + Thread.currentThread().getName());
		}
	};
	
	Runnable r6 = new Runnable() {
		
		public void run() {
			
			System.out.println("执行r6任务的线程名称是:" + Thread.currentThread().getName());
		}
	};
	
	Runnable r7 = new Runnable() {
		
		public void run() {
			
			System.out.println("执行r7任务的线程名称是:" + Thread.currentThread().getName());
		}
	};
	
	Runnable r8 = new Runnable() {
		
		public void run() {
			
			System.out.println("执行r8任务的线程名称是:" + Thread.currentThread().getName());
		}
	};
	
	Runnable r9 = new Runnable() {
		
		public void run() {
			
			System.out.println("执行r9任务的线程名称是:" + Thread.currentThread().getName());
		}
	};
	
	Runnable r10 = new Runnable() {
		
		public void run() {
			
			System.out.println("执行r10任务的线程名称是:" + Thread.currentThread().getName());
		}
	};
	
	executor.execute(r1);
	executor.execute(r2);
	executor.execute(r3);
	
	//上述三行因池中没有线程会创建三个线程1,2,3 	阻塞100毫秒,好让线程1,2,3执行完任务并空闲
	executor.awaitTermination(100, TimeUnit.MILLISECONDS);
	executor.execute(r4);
	executor.execute(r5);
	executor.execute(r6);
	
	//r4,r5,r6执行的时候,刚好有三个空闲线程,则使用空闲线程1,2,3去执行他们
	//然后阻塞70秒,目的是为了演示让线程1,2,3在空闲超时60秒,被销毁了
	executor.awaitTermination(70, TimeUnit.SECONDS);
	
	//所以,r7,r8,r9,r10进来后,线程池里面没有线程了,又创建了4个线程4,5,6,7去执行他们
	executor.execute(r7);
	executor.execute(r8);
	executor.execute(r9);
	executor.execute(r10);
	
	//关闭线程池
	executor.shutdown();
}

控制台打印如下:

 

newScheduledThreadPool (应用在定时调度场景中)

//只执行一次的task

public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);

//只执行一次的task,返回的ScheduledFuture实现Future接口.get方法获取callable的返回值

public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit);

 

//以下两个方法都是启动一个定时执行的任务,initialDelay多久后第一次执行

//区别是:

              scheduleAtFixedRate的period表示上一次执行结束时到下一次开始的时间间隔

              scheduleWithFixedDelay的delay表示上一次开始执行时到下一次开始的时间间隔

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);

public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);

使用示例:

public static void main(String[] args) {

		ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
		Thread t = new Thread(){
			@Override
			public void run() {
				System.out.println(new SimpleDateFormat("dd ss").format(new Date()));
			}
		};

        /*
         * para1 表示一个Runnable或Callable<V>的实现
         * para2 表示线程第一次执行时间间隔
         * para3 表示第一次往后每次执行时间间隔
         * para4 表示时间的单位类型
         */
		executor.scheduleAtFixedRate(t, 3000, 2000, TimeUnit.MILLISECONDS);
		
	}

控制台会打印:

07 42
07 44
07 46
...

 

*线程池中阻塞主线程等待池中线程执行完毕的方法 awaitTermination(long timeout, TimeUnit unit) 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值