一、并行编程可使程序执行速度极大的提升,java自己是一种多线程语言。java
二、使用多线程能够利用机器额外的处理器,资源充分利用。编程
java的线程机制是抢占式的,这表示调度机制会周期性的中断线程,将上下文切换到另外一个线程,从而为每一个线程都提供时间片,使得每一个线程都会分配到数量合缓存
理的时间去驱动它的任务,并发编程使咱们能够将程序划分为多个分离的、独立运行的任务。多线程
public class LiftOff implements Runnable{
//打印100之内的数
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
}
当从Runnable导出一个类时,必须具备run()方法,可是这个方法并没有特殊之处————它不会产生任何内在的县城能力,要实现线程行为,你必须显式地将一个任务附着到线程上。
将Runnable类提交给Thread的构造器,经过Thrad.start()启动线程,调用以后主线程当即返回执行下面的打印逻辑,启动的新线程再执行它的run()方法。并发
public class TestThread {
public static void main(String[] args) {
Thread t = new Thread(new LiftOff());
t.start();
System.out.println("waiting for thread !");
}
}
public class SimpleThread extends Thread {
private int countDown = 5;
private static int threadCount = 0;
public SimpleThread() {
super(Integer.toString(++threadCount)); //经过构造器为线程赋名,更名字能够经过Thread.getName()得到
start();
}
@Override
public String toString() {
return "#" + getName() + "(" + countDown + ") .";
}
@Override
public void run() {
while (true){
System.out.println(this);
if(--countDown == 0) return;
}
}
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new SimpleThread();
}
}
}
若是但愿任务在完成时可以返回一个值,那么能够实现Callable接口而不是Runnable接口。
public class TaskWithResult implements Callable<String> {
private int id;
public TaskWithResult(int id) {
this.id = id;
}
@Override
public String call() throws Exception {
return "result of TaskWithResult is " + id ;
}
}
测试主类:
public class TestCallThread {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
ArrayList<Future<String>> results = new ArrayList<>();
for (int i = 0; i < 10; i++) {
results.add(executorService.submit(new TaskWithResult(i)));
}
for (Future<String> fs : results) {
try {
System.out.println(fs.get());
} catch (InterruptedException e) {
System.out.println(e);
return;
} catch (ExecutionException e) {
e.printStackTrace();
return;
}finally {
executorService.shutdown();
}
}
}
}
ExecutorService.add()方法添加一个任务以后,会返回一个Future,经过Future的Future.isDone()能够查询任务是否已经完成,若是完成了能够调用Future.get(),此时get()方法会阻塞,直至结果准备就绪。
一、每次new Thread() 新建对象,性能差;ide
二、线程缺少统一的管理,可能无限制的新建线程,相互竞争,有可能占用过多系统资源致使死机或OOM(内存溢出);性能
三、缺乏更多的功能,如更多执行,按期执行,线程中断。测试
一、重用 存在的线程,减小对象的建立、消亡的开销,性能佳;this
二、可有效的控制最大并发线程数,提升系统资源利用率,同时能够避免过多资源竞争,避免阻塞;操作系统
三、提供定时执行,按期执行,单线程,并发控制等高级功能。
在实际多线程开发中,使用线程池管理线程是优选方法。
ThreadPoolExecutor
三个重要参数:
corePoolSize:核心线程数量; 线程数少于该值,建立新线程,即便存在空闲线程。
maximumPoolSize:线程最大线程数量;线程数少于该值,且大于corePoolSize,只有当阻塞队列满的时候会建立线程。
workQueue:阻塞队列,存储等待执行的任务,很重要,会对线程池运行过程产生重大影响。当线程达到最大线程数量时,新提交的任务会添加到阻塞队列里,当阻塞队列满的时候,会使用下文的拒绝策略。
keepAliveTime:线程没有任务执行时最多保持多久时间终止。当线程数量大于核心线程数的时候,若是一个线程超过这个时间,没有任务提交,线程即销毁。
unit:keepAliveTime的时间单位。
ThreadFactory:线程工厂,用来建立线程,默认会使用默认的线程工厂来建立线程,使用默认的线程工厂建立线程的时候,线程具备相同的优先级,非守护的线程,具备线程名称的线程。
rejecthandler:当拒绝处理任务时的策略。
a、直接抛出异常(默认策略)(AbortPolicy)
b、用调用者的线程处理任务 (CallerRunsPolicy)
c、丢弃队列中最靠前的任务。(DiscardOldestPolicy)
d、直接丢弃任务。(DiscardPolicy)
若是想使cpu有一个合理的利用:建议设置较大的阻塞队列大小,较小的线程池容量。
public class ExecutorDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(new LiftOff());
executorService.shutdown(); //使线程池继续执行已经提交的任务,可是再也不接受新任务
}
}
用法:
//延迟3秒执行public static void main(String[] args) {
//定义一个实例
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello!");
}
},3,TimeUnit.SECONDS);
}
//延迟1秒,每隔3秒执行一次
public static void main(String[] args) {
//定义一个实例
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("hello!");
}
},1,3,TimeUnit.SECONDS);
}
一、Running:接受新提交的任务,而且也能处理阻塞队列当中的任务;
二、ShutDown:不能再接受新提交的任务,但能提交阻塞队列保存的任务;
三、Stop:再也不接受新提交的任务,也不处理阻塞队列中的任务;
四、Tidying:全部的线程终止,线程池中的工做线程数量为0;
五、Terminated:
一、execute():提交任务,交给线程池执行。
二、submit():提交任务,可以返回执行结果,execute+Future。
三、shutDown():关闭线程,等待任务都执行完。
四、shutDownNow():关闭线程,不等待任务执行完,当即关闭。
如下几个方法能够对线程的实例进行监控:
五、getTaskCount():线程池已执行和未执行的任务总数
六、getCompleteTaskCount():已完成的任务数量
七、getPoolSize():线程池当前的线程数量
八、getActiveCount():当前线程池中正在执行任务的线程数量
一、CPU密集型任务:须要尽可能压榨CPU,参考值能够设为N(CPU + 1);
二、IO密集型任务,参考值能够设置为2*N(CPU);
该方法使线程停止执行给定的时间。
线程的优先级将该线程的重要性传递给了调度器。尽管cpu处理现有线程集的顺序是不肯定的,可是调度器将倾向于让优先级最高的线程先执行。
public class SimplePriorities implements Runnable {
private int countDown = 5;
private volatile double d ;
private int priority;
public SimplePriorities(int priority) {
this.priority = priority;
}
@Override
public String toString() {
return Thread.currentThread() + ": " + countDown;
}
@Override
public void run() {
Thread.currentThread().setPriority(priority);
while (true){
for (int i = 0; i < 100000; i++) {
d += (Math.PI + Math.E) / (double)i;
if(i % 1000 == 0){
Thread.yield();
}
System.out.println(this);
if(--countDown == 0) return;
}
}
}
}
因为考虑到线程级别与操做系统兼容映射的问题,建议调整优先级的时候,只使用MAX_PRIORITY、NORM_PRIORITY、MIN_PRIORITY三种级别。
该方法使建议具备相同优先级的其余线程能够运行,可是建议不必定被采用,继续执行的可能仍是这个线程。
后台线程(守护线程)是指在程序运行的时候在后台提供一种通用的服务的线程,而且这种线程并不属于程序中不可或缺的部分。
特色:非守护线程所有结束时,程序终止,同时会杀死进程中全部的后台线程。
必须在启动线程以前调用 Thread.setDaemon()方法,才能把它设置为守护线程。
isDaemon()能够用来判断一个线程是不是守护线程,若是是一个守护线程,那么他建立的全部线程都是守护线程。
当非守护线程结束以后,会当即关闭正在执行的守护线程,从而不会按照既定的逻辑再次往下走,好比try…catch(){}finally{} fianlly中的语句也不会再终止时执行。