序言:并发编程可以使我们将程序划分为多个分离的、独立的运行任务。通过使用多线程机制,这些独立的任务中的每一个都将有执行线程来驱动。<br />线程:每个分任务都有一个线程来驱动其执行。同时单个线程也可以有多个并发执行的任务。
一·Runnable接口:
线程可以驱动任务执行,所以我们需要实现Runnable接口中的Run方法来定义我们需要执行的任务
public class LiftOff implements Runnable{
private int countDown=10;
private static int taskCount=0;
private final int id=taskCount++;
LiftOff() {
}
LiftOff(int countDown){
this.countDown=countDown;
}
public String status(){
return "Lift#"+id+"("+(countDown>0?countDown:"LiftOff!")+")";
}
@Override
public void run() {//定义了任务
while (countDown-->0){
System.out.println(status());
Thread.yield();
}
}
}
注意:当从Runnable导出一个类的时候,则必须要实现run()方法。但这个方法只是去定义一个任务,本身没有任何内在的线程能力。要实现线程行为,必须要将一个任务显示的附着到线程上(Thread或者Executor)。
二 、Thread类:
Thread是产生线程之一的方法,只需要**接受一个Runnable对象**,同时**调用该类中的start()方法**为线程执行初始化操作,然后会自动调用Runnable中的Run方法。就可以在线程中启动该任务了.
package com.package21.liftOff;
public class ThreadDemo {
public static void main(String[] args) {
Thread thread = new Thread(new LiftOff());
thread.start();
System.out.println("Waiting liftOff!");
}
}
2.1优先级,
可以通过Thread类中的setPriority()方法来设置线程的优先级,同时也可以嗲用getPriority方法来查看线程的优先级, 调度器也将倾向于优先权高的线程执行。
public class SimplePriorities implements Runnable{
private int countDwon=5;
private volatile double d;
private int priority;
SimplePriorities(int priority){
this.priority=priority;
}
@Override
public String toString() {
return Thread.currentThread()+":"+countDwon;
}
@Override
public void run() {
Thread.currentThread().setPriority(priority);//设置线程的优先级
while (true){
for (int i = 0; i <1000 ; i++) {
d+=(Math.PI+Math.E)/(double) i;
if (i % 1000==0)//循环十次会建议让线权
Thread.yield();
}
System.out.println(this);
if (--countDwon==0)return;
}
}
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
// for (int i = 0; i <1 ; i++) {
service.execute(new SimplePriorities(Thread.MIN_PRIORITY));
// }
service.execute(new SimplePriorities(Thread.MAX_PRIORITY));
service.shutdown();
}
}
注意:1.优先级的设定不能设定在构造器中,要在run方法中,因为构造器在run方法之前执行,如果在构造器中设置优先级那就没有任何意义了<br /> 2.只有执行任务的时间长,设置优先级的时候才有时间让线程调度机制来介入。<br /> 3.线程大多数都以默认的优先级运行,视图操作优先级通常是一种错误。
2.2让步
如果一个一个线程的任务已经完成的差不多,这时候可以调用Therad类中的yield()方法来告诉线程“你的工作差不多了,可以让其他线程使用cpu啦!”。但这只是**一个暗示,不能够保证任何机制会采纳。**对于任何的重要控制或在调用整应用时,都不能依赖yeld方法.
2.3后台线程:
是指程序在运行的史诗在后台提供一种服务的线程。
在使用Therad来运行线程的时候,可以通过setDeamon()方法来将该线程设置为后台线程。注意;当所有的非后台线程结束后会杀死进程中的后台线程。也就是说只要有非后台线程在运行程序就不会终止。
public class SimpleDeamos implements Runnable{
@Override
public void run() {
while (true){
try {
TimeUnit.MILLISECONDS.sleep(105);
System.out.println(Thread.currentThread()+" "+this);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i <10 ; i++) {
Thread thread = new Thread(new SimpleDeamos());
thread.setDaemon(true);
thread.start();
}
System.out.println("非后台线程结束");
TimeUnit.MILLISECONDS.sleep(10);
}
}
我们可以发现,将后台线程的休眠时间如果改变的比非后台线程断的话那么他就会去执行。同时,也要在线程启动之前将他设置会后台线程。
注意;在台进程中fianlly子句不会被执行
public class Adeamo implements Runnable{
@Override
public void run() {
try {
System.out.println("Starting ADEAMO");
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
System.out.println("InterruptedException");
}finally {
System.out.println("fianlly语句执行");
}
}
}
class DeamosDontRunFianlly{
public static void main(String[] args) {
Thread thread = new Thread(new Adeamo());
thread.setDaemon(true);//注释之后后天线程开始执行
thread.start();
}
}
2.4TheradFactory:
通过编写定制的TreadFactory可以定制有Executor创建的线程的属性(后台、优先级、名称)。
public class DaemonThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setDaemon(true);
return thread;
}
}
将线程设为后台线程,如果将DeamonThreadFactory作为参数传递到Executor中则所以的线程都将变为后台线程.
public class DeamoFactory implements Runnable{
@Override
public void run() {
while (true){
try {
TimeUnit.MILLISECONDS.sleep(100);
System.out.println("后台线程执行");
} catch (InterruptedException e) {
System.out.println("interrupted");
}
}
}
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool(
new DaemonThreadFactory());
for (int i = 0; i < 10; i++) {
executorService.execute(new DeamoFactory());
}
executorService.shutdown();
System.out.println("主线程执行完毕");
}
}
2.5加入一个线程(join)
在join方法的调用出加入一个线程,知道加入的线程结束,该线程才能继续运行,如:a 线程在b线程上调用join方法,那么如果b线程到达a.join()方法的时候将会等待a线程运行结束,b线程才会向下运行。
package com.package21;//: concurrency/Joining.java
class Sleeper extends Thread {
private int duration;
public Sleeper(String name, int sleepTime) {
super(name);
duration = sleepTime;
start();
}
public void run() {
try {
sleep(duration);
} catch (InterruptedException e) {
System.out.println(getName() + " was interrupted. " +
"isInterrupted(): " + isInterrupted());
return;
}
System.out.println(getName() + " has awakened");
}
}
class Joiner extends Thread {
private Sleeper sleeper;
public Joiner(String name, Sleeper sleeper) {
super(name);
this.sleeper = sleeper;
start();
}
public void run() {
try {
sleeper.join();
} catch (InterruptedException e) {
System.out.println("Interrupted");
}
System.out.println(getName() + " join completed");
}
}
public class Joining {
public static void main(String[] args) {
Sleeper sleepy = new Sleeper("Sleepy", 15000), grumpy = new Sleeper("Grumpy", 1500);
Joiner dopey = new Joiner("Dopey", sleepy), doc = new Joiner("Doc", grumpy);
sleepy.interrupt();//终止该线程
}
}
三、Executor(线程池):
这是我们将定义好的任务附着到线程上的另一种方式。他将替你去管理Thread对象,从而无需显示的去管理线程的生命周期
public class ExecutorDemo {
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 0; i <5 ; i++)
executor.execute(new LiftOff());
executor.shutdown();
}
}
ExectuorService知道如何来执行Runnable对象。只需要使用Executors的静态方法就可以创建该对象。同时CachedThreadPool将为每个任务都创建一个线程。shutdown方法会防止新的任务提交到构建的Executor,同时会讲当前线程中的所有任务完成之后在退出(即使主线程或main方法退出)。
①:CachedThreadPool:
会在程序执行的过程中创建与所需数量相同的线程,然后在回收线程的时候停止创建新线程
②:FixedThreadPool:
可以自己定以开辟的线程数量
public static void Filexd(){
ExecutorService executor = Executors.newFixedThreadPool(2);
for (int i = 0; i <5 ; i++)
executor.execute(new LiftOff());
executor.shutdown();
}
③:SingleThreadExceutor:
保证只使用同一个线程,如果提交了多个任务,那么这些任务将会排队,每个任务都会在下一个任务开始前运行结束。
public static void Single(){
ExecutorService executor = Executors.newSingleThreadExecutor();
for (int i = 0; i <5 ; i++)
executor.execute(new LiftOff());
executor.shutdown();
}
④showdown与showdownNow的区别:
**相同点:**showdown和showdownNow都是作为Executor中来关闭线程池的。且如果线程池中有任务两者都会继续让任务执行。<br /> ** 不同点:**<br /> showutdown在执行之后,不会引发打断异常,而shutdownNow()会引发异常,从而我们可以在异常处理处加入自己的处理。<br />
public class ShowdownVsShowdowNow {
public static void shutDown( ExecutorService service){
service.execute(()->{
System.out.println("线程开始休眠");
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
System.out.println("线程休眠被打断的异常");
return;
}
for (int i = 0; i < 5; i++) {
System.out.println("线程任务还在运行");
}
System.out.println("线程任务执行完毕");
return;
});
System.out.println("shutdown执行");
service.shutdown();
}
public static void shutDownNow( ExecutorService service){
service.execute(()->{
System.out.println("线程开始休眠");
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
System.out.println("线程休眠被打断的异常");
return;
}
for (int i = 0; i < 5; i++) {
System.out.println("线程任务还在运行");
}
});
System.out.println("service.shutdownNow()执行");
service.shutdownNow();
}
public static void main(String[] args) throws InterruptedException {
ExecutorService service = Executors.newCachedThreadPool();
shutDown(service);
TimeUnit.SECONDS.sleep(15);
System.out.println("开始执行shutDownNow()");
ExecutorService service2 = Executors.newCachedThreadPool();
shutDownNow(service2);
}
}
//~input:
shutdown执行
线程开始休眠
线程任务还在运行
线程任务还在运行
线程任务还在运行
线程任务还在运行
线程任务还在运行
线程任务执行完毕
开始执行shutDownNow()
service.shutdownNow()执行
线程开始休眠
线程休眠被打断的异常
Process finished with exit code 0
从运行结果我们可以看出,shutdown并没有引发打断异常出现,而shutdownNow引发了打断异常,从而我们在异常处理去加上了return处理来让程序停止运行。
扩展: Executors每个创建线程方法的对应原理:
1.Executors.newCachedThreadPool();
内部实现:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
2.Executors.newFixedThreadPool(int nThreads);
内部实现
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
3.Executors.newSingleThreadExecutor();
内部实现
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService//包装了一下ThreadPoolExecutor
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
通过以上可得知,创建线程的静态方法其内部都是由ThreadPoolExecutor去实现的。而ThreadPoolExecutor有不同的构造器。其中主要的构造器为:
public ThreadPoolExecutor(int corePoolSize,//核心线程数量
int maximumPoolSize,//允许创建的最大线程数量
long keepAliveTime,//超过核心线程外的空闲线程存活时间
TimeUnit unit,//时间单位
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
所以我们也可以直接new 出**ThreadPoolExecutor**去自己设定线程池的
四、从任务中产生返回值:
Runnable在定义任务的时候,并不回去返回任何值。如果希望任务在完成的时候能够返回一个值那么可以实现**Callable接口**.他是一个具有类型参数的泛型接口,他的类型参数表示从call()方法中返回的值的类型。同时必须要使用submit()方法去调用。
public class TaskWithResult implements Callable<String> {
private int id;
TaskWithResult(int id){
this.id=id;
}
@Override
public String call() throws Exception {
return "TackWithReult"+id;
}
}
class Test{
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService service = Executors.newCachedThreadPool();
ArrayList<Future<String>> arrayList = new ArrayList<>();
for (int i = 0; i <10 ; i++) {
arrayList.add(service.submit(new TaskWithResult(i)));
}
for (Future<String> future : arrayList) {
if (future.isDone()){
String name = future.get();
System.out.println(name);
}else {
System.out.println("获取名字失败");
}
}
System.out.println("程序执行完毕");
}
}
submit方法会先返回一个future对象,当任务完成时会在该对象中返回一个结果。可以调用future中的isDone()方法去判断任务是否完成。完成后可以调用get方法去获取结果。如果任务没有完成此时并没有结果,那么get方法将将阻塞。
五、捕获异常:
由于线程的本质特性,使得你不能捕获从线程中逃逸的异常。
5.1 在javas5中使用Executor就捕获线程异常
public class ExceptionThread implements Runnable{
@Override
public void run() {
System.out.println("====");
throw new RuntimeException();
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(new ExceptionThread());
}
}
5.2 使用Handler接口处理异常
Handler是javaSe5中的新接口,它允许你在每一个Thread对象上都附着一个异常处理器(Thread.UncaughtExceptionHandler)。为捕获的异常会有定义的异常处理来捕获。
class ExceptionThread2 implements Runnable{
@Override
public void run() {
Thread thread = Thread.currentThread();
System.out.println("run() by "+thread);
System.out.println("eh= "+thread.getUncaughtExceptionHandler());
throw new RuntimeException("未捕获的异常信息");
}
}
class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler{//异常处理器
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("捕获的线程异常caught"+e);
}
}
class HandlerThreadFactory implements ThreadFactory{
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
//设置线程处理器
thread.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
System.out.println("捕获的线程异常的处理器="+thread.getUncaughtExceptionHandler());
return thread;
}
}
public class CaptureUncaughtException {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool(new HandlerThreadFactory());
service.execute(new ExceptionThread2());
service.shutdown();
}
}