Java并发(一)——启动多线程

Java并发(一)——启动多线程

实现Runnable接口

public class LiftOff implements Runnable{
	protected int countDown = 10;
	private static int taskCount = 0;
	private final int id = taskCount++;
	public LiftOff(){}
	public LiftOff(int countDown){
		this.countDown = countDown;
	}
	public String status(){
		return "#" + id + "(" + (countDown > 0 ? countDown : "Liftoff!") + ")";
	}
	public void run(){
		while(countDown-- > 0){
			System.out.print(status());
			Thread.yield();
		}
	}
	
	public static void main(String[] args){
		for(int i=0; i<5; i++)
			new Thread(new LiftOff()).start();
		System.out.println("Waiting for LiftOff");
	}
}/* Output 
#1(9)#1(8)#2(9)#0(9)#2(8)#0(8)#0(7)#0(6)#0(5)#0(4)#0(3)#0(2)#0(1)#0(Liftoff!)#1(7)#3(9)Waiting for LiftOff
#2(7)#4(9)#3(8)#1(6)#3(7)#4(8)#2(6)#4(7)#2(5)#2(4)#2(3)#2(2)#2(1)#2(Liftoff!)#3(6)#3(5)#3(4)#3(3)#1(5)#3(2)#3(1)#3(Liftoff!)#4(6)#1(4)#4(5)#4(4)#1(3)#1(2)#1(1)#1(Liftoff!)#4(3)#4(2)#4(1)#4(Liftoff!)<<< Process finished (PID=15764). (Exit code 0)
*///:~

程序中Thread.yield()声明“我已经执行完生命周期中最重要的部分了,此刻正是切换给其他任务执行一段时间的一个大好时机。”其中,Thread构造器只需要一个Runnable对象,并通过start()方法运行对应对象的run()方法。

不过通常使用Executor来实现对线程的管理,并使用execute来提交Runnable对象。

import java.util.concurrent.*;

public class CachedThreadPool{
	public static void main(String[] args){
		ExecutorService exec = Executors.newCachedThreadPool();
		// ExecutorService exec = Executors.newFixedThreadPool();
		for(int i=0; i<5; i++)
			exec.execute(new LiftOff());
		exec.shutdown();
	}
}/*Output 
#1(9)#0(9)#3(9)#4(9)#2(9)#4(8)#3(8)#0(8)#1(8)#0(7)#0(6)#0(5)#3(7)#4(7)#2(8)#4(6)
#4(5)#4(4)#3(6)#0(4)#1(7)#0(3)#3(5)#4(3)#2(7)#4(2)#3(4)#0(2)#1(6)#0(1)#3(3)#4(1)
#4(Liftoff!)#2(6)#3(2)#0(Liftoff!)#1(5)#3(1)#2(5)#3(Liftoff!)#1(4)#2(4)#1(3)#2(3
)#2(2)#1(2)#2(1)#1(1)#2(Liftoff!)#1(Liftoff!)
*///:~

其中shutdown()方法可以防止新任务被提交给这个Executor。同时可以声明SingleThreadExecutor来限制线程数量为1,此时如果对其提交多个任务,那么这些任务将顺序执行。

import java.util.concurrent.*;

public class SingleThreadExecutor{
	public static void main(String[] args){
		ExecutorService exec = Executors.newSingleThreadExecutor();
		for(int i=0; i<5; i++)
			exec.execute(new LiftOff());
		exec.shutdown();
	}
}/*Output 
#0(9)#0(8)#0(7)#0(6)#0(5)#0(4)#0(3)#0(2)#0(1)#0(Liftoff!)#1(9)#1(8)#1(7)#1(6)#1(
5)#1(4)#1(3)#1(2)#1(1)#1(Liftoff!)#2(9)#2(8)#2(7)#2(6)#2(5)#2(4)#2(3)#2(2)#2(1)#
2(Liftoff!)#3(9)#3(8)#3(7)#3(6)#3(5)#3(4)#3(3)#3(2)#3(1)#3(Liftoff!)#4(9)#4(8)#4
(7)#4(6)#4(5)#4(4)#4(3)#4(2)#4(1)#4(Liftoff!)
*///:~

实现Callable接口

如果需要线程具有返回值,可实现Callable接口

import java.util.concurrent.*;
import java.util.*;

class TaskWithResult implements Callable<String>{
	private int id;
	public TaskWithResult(int id){
		this.id = id;
	}
	public String call(){
		return "result of TaskWithResult " + id;
	}
}

public class CallableDemo{
	public static void main(String[] args){
		ExecutorService exec = Executors.newCachedThreadPool();
		ArrayList<Future<String>> results = new ArrayList<Future<String>>();
		for(int i=0; i<5; i++)
			results.add(exec.submit(new TaskWithResult(i)));
		for(Future<String> fs : results){
			try{
				System.out.println(fs.get(200, TimeUnit.MILLISECONDS));
			}
			catch(TimeoutException e){
				System.out.println(e);
				return;
			}
			catch(InterruptedException e){
				System.out.println(e);
				return;
			}
			catch(ExecutionException e){
				System.out.println(e);
			}
			finally{
				exec.shutdown();
			}
		}
	}
}/* Output 
result of TaskWithResult 0
result of TaskWithResult 1
result of TaskWithResult 2
result of TaskWithResult 3
result of TaskWithResult 4
*///:~

使用submit()来提交Callable对象,并将返回值放入Future中。同时,可以通过Future对象get()来获取返回值,并指定运行时间。同时,submit()也能够提交Runnable对象,但是此时由于没有返回值,调用方式为

Future<?> f = exec.submit(Runnable())

不过此时将不能使用get方法来获取返回值。其通常的作用是通过f来调用cancel方法来实现对指定线程发送中断信号。

休眠

public void run(){
	try{
		// Old-style
		Thread.sleep(100);
		// Java SE5/~-style
		TimeUnit.MILLISECONDS.sleep(100);
	}
	catch(InterruptedException e){
		System.err.println("Interrupted");
	}
}

优先级

import java.util.concurrent.*;

public class SimplePriorities implements Runnable{
	private int countDown = 5;
	private volatile double d;
	private int priority;
	public SimplePriorities(int priority){
		this.priority = priority;
	}
	public String toString(){
		return Thread.currentThread() + ": " + countDown;
	} 
	public void run(){
		Thread.currentThread().setPriority(priority);
		while(true){
			for(int i=1; i<100000; i++){
				d += (Math.PI + Math.E)/(double)i;
				if(i%1000 == 0)
					Thread.yield();
			}
			System.out.println(this);
			if(--countDown == 0) return;
		}
	}
	public static void main(String[] args){
		ExecutorService exec = Executors.newCachedThreadPool();
		for(int i=0; i<5; i++)
			exec.execute(new SimplePriorities(Thread.MIN_PRIORITY));
		exec.execute(new SimplePriorities(Thread.MAX_PRIORITY));
		exec.shutdown();
	}
}/* Output 
Thread[pool-1-thread-1,1,main]: 5
Thread[pool-1-thread-6,10,main]: 5
Thread[pool-1-thread-5,1,main]: 5
Thread[pool-1-thread-5,1,main]: 4
Thread[pool-1-thread-5,1,main]: 3
Thread[pool-1-thread-2,1,main]: 5
Thread[pool-1-thread-3,1,main]: 5
Thread[pool-1-thread-4,1,main]: 5
Thread[pool-1-thread-5,1,main]: 2
Thread[pool-1-thread-2,1,main]: 4
Thread[pool-1-thread-1,1,main]: 4
Thread[pool-1-thread-6,10,main]: 4
Thread[pool-1-thread-5,1,main]: 1
Thread[pool-1-thread-2,1,main]: 3
Thread[pool-1-thread-3,1,main]: 4
Thread[pool-1-thread-4,1,main]: 4
Thread[pool-1-thread-6,10,main]: 3
Thread[pool-1-thread-2,1,main]: 2
Thread[pool-1-thread-1,1,main]: 3
Thread[pool-1-thread-6,10,main]: 2
Thread[pool-1-thread-2,1,main]: 1
Thread[pool-1-thread-1,1,main]: 2
Thread[pool-1-thread-4,1,main]: 3
Thread[pool-1-thread-3,1,main]: 3
Thread[pool-1-thread-6,10,main]: 1
Thread[pool-1-thread-4,1,main]: 2
Thread[pool-1-thread-1,1,main]: 1
Thread[pool-1-thread-3,1,main]: 2
Thread[pool-1-thread-4,1,main]: 1
Thread[pool-1-thread-3,1,main]: 1
*///:~

由于本系统是多核处理器,所以没有出现先完全执行pool-1-thread-6的情形。需要注意的是,唯一可移植的优先级只包括MAX_PRIORITY,NORM_PRIORITY,MIN_PRIORITY。

后台(daemon)线程

后台线程并不属于程序中不可或缺的部分。因此当所有非后台线程结束时,程序也就终止,同时会杀死进程中的所有后台线程(直接终止)。在启动后台线程时,必须在启动前先使用 daemon.setDaemon(true) 将线程设置为后台线程。同时,如果是一个后台线程,那么它创建的任何线程都将自动设置为后台线程。

其他实现线程的方式

除了以上显示定义并启动线程的方式,还可以通过继承Thread、实现Runnable接口或者内部类(匿名内部类)来实现。

join()方法

ThreadName.join():调用线程将等待ThreadName线程结束之后再运行

ThreadName.interrupt():中断ThreadName线程的执行

异常捕获

import java.util.concurrent.*;

class ExceptionThread implements Runnable{
	public void run(){
		Thread t = Thread.currentThread();
		System.out.println("run() by " + t);
		System.out.println("eh = " + t.getUncaughtExceptionHandler());
		throw new RuntimeException();
	}
}

class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler{
	public void uncaughtException(Thread t, Throwable e){
		System.out.println("caught " + e);
	}
}

class HandlerThreadFactory implements ThreadFactory{
	public Thread newThread(Runnable r){
		System.out.println(this + " creating new Thread");
		Thread t = new Thread(r);
		System.out.println("created " + t);
		t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
		System.out.println("eh = " + t.getUncaughtExceptionHandler());
		return t;
	}
}

public class CaptureUncaughtException{
	public static void main(String[] args){
		ExecutorService exec = Executors.newCachedThreadPool(new HandlerThreadFactory());
		exec.execute(new ExceptionThread());
		// define a default uncaught exception handler
		// Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
		// ExecutorService exec = Executors.newCachedThreadPool();
		// exec.execute(new ExceptionThread());
		
	}
}/* Output 
HandlerThreadFactory@4e25154f creating new Thread
created Thread[Thread-0,5,main]
eh = MyUncaughtExceptionHandler@70dea4e
run() by Thread[Thread-0,5,main]
eh = MyUncaughtExceptionHandler@70dea4e
HandlerThreadFactory@4e25154f creating new Thread
created Thread[Thread-1,5,main]
eh = MyUncaughtExceptionHandler@7a88bb53
caught java.lang.RuntimeException
*///:~

线程run()方法中的异常需要通过设置UncaughtExceptionHandler来捕获,也可以通过定义一个默认的未捕获异常处理器,在没有定义专有处理器时被调用。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值