Java中的线程整理

84 阅读3分钟

Java中的线程整理

Runnable和Callable

我们最了解的实现多线程的方法要么是继承Thread,要么是实现Runnable或Callable,将一个类直接转化为线程类,然后在重写的run方法和call方法中编写任务逻辑。

public class ColdTaskThread extend Thread{
    @Override
    public void run(){
        TimeUnit.Second.sleep(3);
        System.out.println("cold task invoke");
    }
    @Test
    public void test(){
        new ColdTaskThread().start();
    }
}

实现Runnable和Thread逻辑相同,而实现Callable稍有不同

@Test
public void testFutureCall() throws ExecutionException,InterruptedException{
    Callable<String> call = new Callable({
        @Override
        public String call() throws Exception(){
            TimeUnit.SECOND.sleep(2);
            return "call invoke"l;
        }
    });
    FutureTask future = new FuturetTask(call);
    System.out.println(future.isDone());
    new Thread(future).start();
    System.out.println(future.isDone());
    System.out.println(future.get());
    System.out.println(future.isDone());
}

相同点: 以上两种方式都会不约而同地去启动线程类,执行对应任务。但是,这样带来的结果就是一个线程与一个逻辑任务强绑定,即想要执行A任务就必须启动A线程,执行B任务就必须启动B线程······,任务达到一定数量会新建大量线程,而每一个任务执行的时间又不确定,可能短暂、可能长久、还可能阻塞,频繁创建执行时间短暂的任务的线程无疑加大线程上下文切换的开销,同时管理起来也不方便,由此引出Java在JUC包中为我们提供的线程池相关包
不同点:callable创建线程的方式调用任务后可以通过FutureTask提供对执行结果信息的获取,因此FutureTask类在callable显得十分重要;而runnable接口在任务执行完以后没有返回值,如果两个线程之间需要通信只能走共享数据这一条路。另外,需要注意的是能提供线程启动的只有Runnable接口和Thread方法!

//继承了Runnable,可以作为线程执行类
public class FutureTask<V> implements RunnableFuture<V> {
    //具有的方法
    public boolean isCancelled(){
        return state >= cencelled;
    };
    //检验任务是否完成
    public boolean isDone(){
        return state != NEW;
    }
    //获取call方法返回的结果
    public V get(){
        int s = state;
        if(s <= COMPLETIGN)
            s = awaitDone(false,0l);
         return report(s);
    }
    
}

FutureTask类追溯它的继承父类和实现接口后发现它最终继承的是Future,实现的是Runnable,也就是说通过callable接口实现多线程最终还是需要依靠runnable接口。new Thread(future).start(); \

线程池

Java提供了多种线程池类型给我们,我们可以通过Executors获得对应的线程池

public class Executors{
    public static ExecutorService newFixedThreadPool(int nThreads){
        return new ThreadPoolExecutor(nThread,nThread,0L,TimeUnit.MILLSECONDS,
                            new LinkedBlockingQueue<Runnable>());
    }
    public static  ExecutorService newSingleThreadExecutor(){
        return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLSECONDS,new LinkedBlockingQueue<Runnable>))
    };
    public static ExecutorService newCachedThreadPool(){
        return new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.SECONDS,new SynchronousQueue<Runnable>);
    }
    public static ScheduledExecutorService newSingleThreadScheduledExecutor(){
        return new DelegatedScheduledExecutorService(
            new ScheduledThreadPoolExecutor(1)
        );
    }
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize){
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }    
}

具体用法: ExecutorService与ScheduledExecutorService稍有不同,后者继承前者,后者用来处理任务周期执行。

//scheduleWithFixedDelay四个参数,第一个参数是任务对象,
//第二个参数是第一次执行延时的时间,如果在这个时间之前主线程已经执行完成那么该线程不会执行,
//第三个参数是距离上一次任务执行完相距多少秒继续执行,第四个参数是时间单位
ScheduleExecutorService ses = Executors.newScheduledThreadPool(12);
ses.scheduleWithFixedDelay(()->{
   System.out.println("is is good good ");
    },1,4,TimeUnit.SECONDS);
//一次性执行
ScheduleExecutorService ses = Executors.newSchedueledThreadPool(21);
ses.schedule(()->{},1,TimeUnit.SECONDS.sleep(1));
//核心线程数和最大线程数相同
ExecutorService es = Executors.newFixedThreadPool(2);
for(int i = 0;i<5;i++)
{
    es.submit(()->{
        System.out.println(Thread.currentThread().getName()+"--"+new Random().nextInt(20));
    });
}

//最大线程数很大,可以重复利用闲置的已创建好的线程资源,适合大量短时任务
ExecutorService es = Executors.newCachedThreadPool();
//线程池内只支持一个线程执行任务
ExecutorServiuce es = Executors.newSingleThreadPool();
//线程池内支持一个线程执行周期任务
ScheduledExecutorService es = Executors.newSingleScheduledThreadPool();

以上就是Java支持的线程池类型