21章 并发

1、 基本线程机制

并发编程使我们可以将程序划分为多个分离的,独立运行的任务。通过使用多线程机制,这些独立的任务中的每一个都将由执行线程来驱动。一个线程就是在进程中的一个单一的顺序控制流,因此,单个进程可以拥有多个并发执行的任务,但是你的程序使得每个任务都好像有其自己的cpu一样。其底层机制是切分cpu时间,但通常你不需要考虑它。

 线程模型为编程带来了便利,它简化了在单一程序中同时交织在一起的多个操作的处理。在使用线程时候,cpu将轮流给每个任务分配其占用时间。每个任务都觉得自己在一直占用cpu,但实时上cpu时间是划分成片段分配给了所有的任务(例外情况程序确实运行在多个cpu上)。线程的一大好处是可以使你从这个层次抽身出来,既代码不必知道它是运行在具有一个还是多个cpu的机器上。所以,使用线程机制是一种建立透明,可扩展的程序的方法,如果程序运行太慢,为机器增添了一个cpu就能很容易地加快程序的运行速度。多任务和多线程往往是使用多处理器系统的最合理方式

2、 定义任务

需要实现Runnable接口,然后提交给Thread构造器,调用start方法。

public class LiftOff implements Runnable {
	protected int countDown = 10;
	private static int taskCount = 0;
	private final int id = taskCount ++;
	public LiftOff(int countDown){
		this.countDown = countDown;
	}
	 public LiftOff() {
		// TODO Auto-generated constructor stub
	}
	public String status(){
		return "#" + id +"(" + (countDown >0 ? countDown :"Liftoff!") + "),";
	}
	
	@Override
	public void run() {
		while (countDown -- > 0) {
			System.out.println(status());
			Thread.yield();//让步,将cpu给其他线程。
		}
	}

}



public class BasicThreads {
	public static void main(String[] args) {
		Thread t = new Thread(new LiftOff());
		t.start();
		System.out.println("Waiting for LiftOff");
	}
}

 3、使用Executor

java.util.concurrent 包中的执行器(Executor)将为你管理Thread对象,从而简化并发编程

public class CachedThreadPool {
	public static void main(String[] args) {
		ExecutorService exec = Executors.newCachedThreadPool();
		for(int i =0 ; i< 5; i++)
			exec.execute(new LiftOff());
		exec.shutdown();//结束这个Executor,防止新的任务提交给这个Executor.
	}
}




public class FixedThreadPool {
	public static void main(String[] args) {
		ExecutorService exec = Executors.newFixedThreadPool(5); //提前分配5个线程
		for(int i =0 ; i< 5; i++)
			exec.execute(new LiftOff());
		exec.shutdown();//结束这个Executor,防止新的任务提交给这个Executor.
	}
}


public class SingleThreadPool {
	public static void main(String[] args) {
		ExecutorService exec = Executors.newSingleThreadPool(); //创建单个线程
		for(int i =0 ; i< 5; i++)
			exec.execute(new LiftOff());
		exec.shutdown();//结束这个Executor,防止新的任务提交给这个Executor.
	}
}

CachedThreadPool 在程序执行过程中通常会创建与所需要数据相同的线程,然后在它回收旧线程时停止创建新的线程,因此它是合理的Executor的首选。SingleThreadExecutor就像是线程为1的FixedThradPool,如果向它提交了多个任务,那么这些任务将排队,每个任务都会在下一个任务开始之前运行结束,所有的任务将使用相同的线程。

4.从任务中产生返回值

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

public class CallableDemo{
	public static void main(String[] args) {
		ExecutorService exec = Executors.newCachedThreadPool();
		ArrayList<Future<String>> results = new ArrayList<>();
		for (int i = 0; i < 10; i++) 
			results.add(exec.submit(new TaskWithResult(i)));
			for (Future<String> future : results) {
				try {
					System.out.println(future.get());
				} catch (Exception e) {
					System.out.println(e);
				}finally {
					exec.shutdown();
				}
			}
		
	}

}

如果需要返回一个值,则需要实现Callable接口,然后调用ExecutorService的submit方法,返回一个Future对象,然后用get方法获取返回值。

5、休眠、优先级、让步

影响任务行为的一种简单方法是调用sleep(),这将使任务中止执行给定的时间。

Thread.sleep(毫秒);

线程的优先级将线程的重要性传递给了调度器。尽管cpu处理现有线程集的顺序是不确定的,但是调度器将倾向于让优先权最高的线程先执行。

Thread.currentThread().setPriority(优先级); //修改当前线程的优先级。

当程序已经完成了最重要的工作,可以给线程调度机制一个暗示,你的工作已经做的差不多了,可以让别的线程使用cpu了。这个暗示通过调用yield()方法来作出,

Thread.yield();//让步,将cpu给其他线程。

6 后台线程(守护线程)

所谓的后台线程,是指在程序运行的时候在后台提供一种通用服务的线程,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非后台线程结束时候,程序也就终止了,同时会杀死进程中所有的后台线程,必须在start之前设置为守护线程。

public class SimpleDaenoms implements Runnable{
	
	@Override
	public void run() {
		try {
			while(true){
				TimeUnit.MILLISECONDS.sleep(100);
				System.out.println(Thread.currentThread() + "" + this);
			}
		} catch (Exception e) {
			System.out.println(e);
		}
		
	}
	
	public static void main(String[] args) {
		for (int i = 0; i < 10; i++) {
			Thread daemon = new Thread(new SimpleDaenoms());
			daemon.setDaemon(true); //设置线程为后台线程。且必须在start之前设置
			daemon.start();
		}
	}

将线程转换为守护线程(后台线程)可以通过调用Thread对象的setDaemon(true)方法来实现。在使用守护线程时需要注意一下几点:

(1) thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。

(2) 在Daemon线程中产生的新线程也是Daemon的。

(3) 守护线程应该永远不去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。

7、编码的变体

可以直接继承Thread类从而实现run方法开启线程

public class SimpleThread extends Thread{
	private int countDown = 5;
	private static int threadCount = 0;
	public  SimpleThread() {
		super(Integer.toString(++ threadCount));
		start();
	}
	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();
		}
	}
}

8 加入一个线程

一个线程可以在其他线程之上调用join()方法,其效果是等待一段时间直到第二个线程结束才继续执行,如果某个线程在另为一个线程t上调用t.join ,此线程将被挂起,直到目标线程t结束才恢复。

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 (Exception 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 (Exception 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", 1500),
			grumppy = new Sleeper("Grumpy", 1500);
	Joiner
		dopey = new Joiner("Dopey", sleepy),
		doc = new Joiner("Doc", grumppy);
	grumppy.interrupt();
	}

9 捕获异常

由于线程的本质特性,使得你不能捕获从线程中逃逸的异常,一旦异常逃出run方法,它就会向外传播到控制台,可以用Thead.UncaughtExceptionHandler.uncaughException() 来捕获线程中的异常。

class ExeceptionThread2 implements Runnable{
	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{
	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 executorService  = Executors.newCachedThreadPool(new HandlerThreadFactory());
		executorService.execute(new ExeceptionThread2());
	}
}

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值