并发浅谈(一)

基本的线程机制

 1.定义任务

     我们知道线程是可以驱动任务的,因此我们需要一种描述任务的方式,一种方式是使用Runnable接口,并实现其run()方法,此方法是没有返回值的。

       public class Task1 implements Runnable{

                    public void run(){ System.out.println("执行任务")}

       }

     我们实现了run()方法,但此方法并没有特殊之处,他不会产生任何线程的能力,要实现线程的行为,就必须将此任务附着到一个线程上去。

    public  class  Demo {

         public static  void main (String [] args){

                       Thread t = new Thread(new Task1());

                       t.start();

         }

 }


 2.使用Executor

         定义完任务,同时我们也能将任务附着到一个线程上去。在我们将任务附着到一个线程上去时,我们显式的创建一个线程对象。在Java SE5/6中启动任务的优选方案是使用Executor对象。此对象的作用是帮你管理Thread对象,使你不用显式的去声明一个Thread对象。

   public class CachedThreadPool {

          public static  void main (String [] args){

                  ExecutorService exec = Executors.newCachedThreadPool();

                  exec.execute(new Task1());                    

         }

}

我们可以看到 表面上我们是通过ExecutorService 对象来执行任务,其实是此对象只是构建了一个能够执行任务的环境。其底层还是通过将任务附着到线程上执行任务。与命令模式是一样的,任务暴露了要执行的方法,而ExecutorService对象 构建恰当的上下文环境来执行此任务。


在上述代码中,我们用到了newCachedThreadPool()方法,此方法会在程序执行的时候通常会创建与所需数量相同的线程,然后在他回收旧线程的时候停止创建新的线程。

同时还有另外两种方法 newFixedThreadPool(),此方法可以限制线程的数量,通过向构造函数中传递整数来规定创建线程的数量,newSingleThreadExecutor()方法就像是线程数量为1的FixedThreadPool。


3.从任务中产生返回值

 在上述 中创建的任务是没有返回值的,但有时候我们希望任务执行后能返回一个值。这时我们可以实现Callable接口,在Java SE5 中引入的这个接口是具有类型参数的泛型,

他的类型参数是定义返回值的类型的。但必须通过ExecutorService.submit()方法调用 。

    public  class Task2 implements Callable<E>{

            public  <E>  call(){

                         return “返回值”;

            }   

}

public  class  Demo {

       public static void main(String [] args){

           ExecutorService execu = Executors.newCachedThreadPool();

           Future ture =   execu.submit(new Task2());

           ture.get();

       }

}

可以看到任务的返回值被封装到Future 对象中,我们可以同过此对象的get()方法将返回值取出;取出的返回值类型为String 类型

4.休眠,让步

 前面一直说的是任务如何创建 ,如何将任务附着到一个线程上。现在我们简单来看一下影响任务行为的一种简单方法就是调用sleep()方法,此方法可以将任务终止执行给定的时间。调用方式是在run()方法中 添加Thread.sleep(time);  在Java SE5 中引入了更加显式的sleep()版本,它作为TimeUnit类的一部分,同时此方法可以指定sleep()延迟的时间单位。同时另一种行为是通过影响线程,进而影响任务的执行,那就是yield()方法,此方法是暂停当前正在执行的线程对象(从运行状态,回到可运行状态),并给其他优先级相同的线程执行的机会,但这个方法不是绝对的。你调用的此方法只是让其他线程有了执行的机会,但是否会执行其他线程,你是控制不了的。 刚才我们我们提到了线程的优先级, 是通过setPriority()方法来设置的。


5.后台线程

 就像在一部电影中总是少不了群主演员。在程序中也存在这种地位的线程,他们被称之为后台(daemon)线程;这种线程并不属于程序中不可或缺的部分。当所有非后台线程结束时,不管后台线程是否执行完毕,都会被终止。那么设置后台线程的方式是在线程进入可运行状态时通过setDaemo()方法,传递布尔类型的值。来标识是否为后台线程。并且通过后台线程任务产生的线程默认也是后台线程。


6.捕获异常

    由于线程的本质特性,使得你不能捕获从线程中抛出的异常。但在Java SE5中引入一个新的接口 Thread.UncaughtExceptionHandler,此接口可以让你在每个Thread对象上附着一个异常处理器通过uncaughtException()方法在线程因未捕获的异常而临近终止时被调用。

class  ExceptionThread2 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 MyuncaughtException implements Thread.UncaughtExceptionHandler{

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

    @Override
    public Thread newThread(Runnable r) {
        // TODO Auto-generated method stub
        System.out.println(this.getClass().getSimpleName() + "  creating new Thread ");
        Thread t = new Thread(r);
        System.out.println("created "+ t);
        t.setUncaughtExceptionHandler(new MyuncaughtException());
        System.out.println("eh= "+ t.getUncaughtExceptionHandler());
        return t;
    }

}
public class CatureuncaughtException {

    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool(new HandlerThreadFactory());
        
        executor.execute(new ExceptionThread2());
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值