java 并发一 基本的线程机制

  1. Executor
    executor是java5 concurrent包下的执行器提供管理Thread功能,如下例子
public class ThreadPool {
	
	private static int num = 0;

	public static void main(String[] args) {
		//创建线程池
		ExecutorService e = Executors.newCachedThreadPool();
		Task tack = new Task();
		for (int i = 0; i < 1; i++) {
			e.execute(tack); //执行任务
		}
		e.shutdown(); //阻止执行新任务,已经启动的任务依旧会执行完成
		//e.execute(tack); 再次开启任务会抛出RejectedExecutionException
	}
	
	static class Task implements Runnable{

		@Override
		public void run() {
			// TODO Auto-generated method stub
			while(true) {
				if(num == 100)break;
				System.out.println(++num);
			}
			
		}
		
	}

}

我们在这使用了Executor的静态方法newCachedThreadPool创建了一个线程池对象ExecutorService,又调用了其execute方法执行了任务Tack

	/**
		* 返回了一个ThreadPoolExecutor对象(ExecutorService的子类)
		* 线程池下标从0开始,最大值为Integer.MAX_VALUE int 类型的最大值
		* 使用SynchronousQueue来存储任务
		*/
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

newCachedThreadPool方法其实就是new了一个ThreadPoolExecutor对象调用了其构造方法,设置了线程池的参数。相对这个设置了int 最大值的线程池,当然是用于不确定又多少个任务而设置的,当你明确的知道有多少个任务时,提供了newFixedThreadPool( int size)方法这样就不需要初始化大容量的线程池了

	public static void main(String[] args) {
		//创建线程池
		//ExecutorService e = Executors.newCachedThreadPool();
		//线程池大小固定5
		ExecutorService e = Executors.newFixedThreadPool(5);
		Task tack = new Task();
		for (int i = 0; i < 5; i++) {
			e.execute(tack); //执行任务
		}
		e.shutdown(); //阻止执行新任务,当已经启动的任务依旧会执行完成
		//e.execute(tack); 再次开启任务会抛出RejectedExecutionException
	}

有了newFixedThreadPool,我们就可以根据自己的需要去创建相对应大小的线程池了。需注意一点的是,线程是可以复用的,在完成了任务之后依旧会存在于线程池等待新的任务。
相对应的Executors还提供了singleThreadExecutor方法只创建大小为1的线程池,适用于单独的并且长期运行的线程

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

如果向singleThreadExecutor提交了多个任务,那么这些任务将排队依次执行,singleThreadExecutor会根据提交的顺序序列化任务

  1. 从任务中产生返回值
    Runnable是执行独立的任务,但如果需要在执行完任务时能够得到一个返回值则任务类就需要实现Callable接口
	static class Task2 implements Callable<Integer>{

		public Integer call() throws Exception {
			// TODO Auto-generated method stub
			System.out.println("执行了callable");
			Thread.sleep(1000*6);
			return 1;
		}
		
	}
		public static void main(String[] args) throws InterruptedException, ExecutionException {
		ExecutorService es = Executors.newSingleThreadExecutor();
		Task2 task2 = new Task2();
		Future<Integer> future = es.submit(task2);
		int a = future.get();
		System.out.println(a);
		//e.submit(tack);
	}

ExecutorService 得submit方法接受一个任务并调用线程执行它,它返回一个Future对象,Future封装了call方法得返回值并提供get方法获取它。任务没有完成将一直阻塞在get方法这里。但为什么要提供一个Future对象来获取返回值,不能够直接取得返回值吗,原因时Future除了提供get方法获取返回值外,还提供着cancel(),isDone(),isCancelled()等方法,可以通过这些方法来判断是否取消任务还是继续等待

		ExecutorService es = Executors.newSingleThreadExecutor();
		Task2 task2 = new Task2();
		Future<Integer> future = es.submit(task2);
		if(future.isDone()) {
			int a = future.get();
			System.out.println("任务返回值"+a);
		}else {
			future.cancel(true);
			//int a = future.get();
			System.out.println("中断了");
		}

使用cancel方法就可以直接取消任务,如果任务没有执行,那么就不会执行任务,这样就不会一直阻塞在get方法这里。我们来看看Future得实现类FutureTask

public class FutureTask<V> implements RunnableFuture<V> 
public interface RunnableFuture<V> extends Runnable, Future<V> 

其实FutureTask也实现了Runnable,那么我们几乎就可以想到,FutureTask的run方法肯定调用了call()方法并将返回值提供给get()方法获取

    public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable; //
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call(); //执行任务取得结果
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }
    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }

可以看到Future其实也是一个Runnable,由run()方法调用call()执行任务取得返回值,get()方法中,如果任务没有完成将阻塞一直等待到任务完成并将结果返回.

  1. 休眠
public class SleepTask implements Runnable {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ExecutorService e = Executors.newCachedThreadPool();
		for (int i = 0; i < 4; i++) {
			e.execute(new SleepTask());
		}
		e.shutdown();
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		Random r = new Random();
		int ran1 = r.nextInt(10);
		try {
			//Thread.sleep(ran1);
			TimeUnit.SECONDS.sleep(ran1);//其实也是调用的Thread.sleep();
			System.out.println(ran1+"秒");
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	

}

java 5之后提供了TimeUnit枚举来代替了Thread.sleep ,当然除了sleep,还有join,wait,等方法并且还提供了时间的相互转换等方法

			long s = TimeUnit.SECONDS.toMinutes(60);//秒转分
			long f = TimeUnit.MINUTES.toHours(60);//分转小时
			TimeUnit.SECONDS.timedJoin(new Thread(runnable), 20);//将等待指定线程先执行完
			TimeUnit.SECONDS.timedWait(new Object(), 20);//指定锁,线程将阻塞,直到被唤醒或超时
  1. 后台线程(守护线程)
    后台线程时程序运行的时候在后台运行的线程,其不属于程序不可缺的部分,当所有的非后台线程完成时(注意时所有的,不仅仅指创建后台线程的父线程)后台线程会强制结束
public class DaemonThread implements Runnable{

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		/*ExecutorService es = Executors.newCachedThreadPool(new ThreadFactory())
		es*/
		Thread t = new Thread(new DaemonThread());
		t.setDaemon(true);
		t.start();
		
	}
	
	public void run() {
		System.out.println("执行了任务");
	}

}

如上就显式的开启了一个后台线程也就是守护线程,当所有的主线程全部结束时,那么jvm的线程也将结束,会强制杀死所有的后台线程。

  1. ThreadFactory
    线程工厂可以让你的线程池创建指定类型的线程,jdk提供了ThreadFactory接口来提供指定类型的线程的功能,我们先来看看ThreadFactory接口
public interface ThreadFactory {
	//只有一个方法,接收一个任务,返回一个线程对象
    Thread newThread(Runnable r);
}
public class DaemonThread implements Runnable,ThreadFactory{

	private int num = 0;
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		DaemonThread dt = new DaemonThread();
		ExecutorService es = Executors.newCachedThreadPool(dt);
		for (int i = 0; i < 9; i++) {
			es.execute(dt);
			
		}
		es.shutdown();
/*		Thread t = new Thread(new DaemonThread());
		t.setDaemon(false);
		t.start();*/
		
	}
	
	public void run() {
		try {
			TimeUnit.MILLISECONDS.sleep(10);//休息10毫秒
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			System.out.println("强制结束是不会执行finally语句的"+ ++num);
		}
		System.out.println("执行了任务"+ ++num);
	}

	@Override
	public Thread newThread(Runnable r) {
		// TODO Auto-generated method stub
		Thread t = new Thread(r);
		t.setPriority(10);//设置优先级
		t.setDaemon(true);//设置为后台线程
		return t;
	}
}

如上我们就通过使用线程工厂来创建同一种类型的线程了,ExecutorService es所创建的线程都属于后台线程并且优先级最高,但所有的任务都休息了10毫秒,这将导致main线程先跑完,那么所有的后台线程都不会执行(这里注意当后台线程强制退出是不会执行finally语句的)

  1. 中断线程
public class InterruptThread implements Runnable{

	public static void main(String[] args) throws InterruptedException {
		// TODO Auto-generated method stub
		Thread t = new Thread(new InterruptThread());
		t.start();
		//t.interrupt(); 
		System.out.println(t.isInterrupted()); //返回t线程是否中断了
		t.interrupt();							//中断t线程
		System.out.println(t.isInterrupted());//
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub 
		System.out.println(Thread.currentThread().getName()+"开始任务");
		try {
			Thread.interrupted(); //返回是否中断了线程,如果中断了返回true并清除中断标记,也就是不会中断线程了
			TimeUnit.MILLISECONDS.sleep(5);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.out.println(Thread.currentThread().getName()+"线程被中断"+Thread.currentThread().isInterrupted());
		}
		System.out.println(Thread.currentThread().getName()+"结束任务");
	}

}

interrupt()会中断一个阻塞的线程,并抛出InterruptException 。isInterrupted()方法能够判断出该线程是否标记了中断状态,但在异常被捕获时会清除掉这个标记,这也可以理解catch 语句里当然也属于这个线程,它需要执行当然要清除标记不让它标记该线程已中断。Thread还有一个静态方法interrupted()它也是返回当前线程是否标记为中断

    public static boolean interrupted() {
        return currentThread().isInterrupted(true);//true表示确认清除标记,false不清除标记
    }

可以看到其实interrupted是调用的当前执行线程的isInterrupted方法,并会将标记清除掉。
注 : 中断线程只抛出异常,并不会退出该线程

  1. 捕获异常
    由于线程的特性,各自分开,独立运行。所以并不能捕获从线程中逃逸的线程,也就是异常必须在run方法中处理.为了解决这个问题 java1.5提供了UncaughtExceptionHandler接口,它允许每个线程依附一个线程处理器,线程处理器可以取得改线程抛出的异常
public class ExceptinThread implements Runnable{

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ExecutorService es = Executors.newCachedThreadPool(new ExceptionThreadFactory());
		es.execute(new ExceptinThread()); //执行任务
		es.shutdown();
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		throw new RuntimeException("抛出了异常");
	}


}

class UncaughtException implements UncaugxianhtExceptionHandler{
	private Throwable throwable ;
	@Override
	public void uncaughtException(Thread t, Throwable e) {
		// TODO Auto-generated method stub
		System.out.println("caught"+e);
		throwable = e;
	}
}


class ExceptionThreadFactory implements ThreadFactory{

	@Override
	public Thread newThread(Runnable r) {
		// TODO Auto-generated method stub
		UncaughtException ue = new UncaughtException();
		Thread t = new Thread(r);
		t.setUncaughtExceptionHandler(ue); //设置线程处理器
		return t;
		
	}
}

UncaughtException 实现了UncaugxianhtExceptionHandler,这样我们就可以在uncaughtException方法中取得任务run中抛出的异常做进一步的处理,异常处理器可以由线程的setUncaughtExceptionHandler方法设置。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值