JUC之线程、线程池

一、start与run方法

start方法开启一个新线程,异步执行。

run方法同步执行,不会产生新的线程。

start方法只能执行一次,run方法可以执行多次。

二、一些方法

sleep() 线程睡眠

两种方式调用:

Thread.sleep(1000);

TimeUnit.SECONDS.sleep(1);

线程打断

interrupt()这种方式打断,线程会抛出InterruptedException异常。比如在线程睡眠的时候,调用interrupt(),线程抛出异常。抛出InterruptedException异常的时候会清除中断标记。

interrupt()方法仅仅是将线程的中断状态设置为true,并不会停止线程。

thread.interrupt();

还有两个方法

isInterrupted()判断当前线程是否被打断,不会清除打断标记。

interrupted();判断当前线程是否被打断,清除打断标记,静态方法。清除打断标记就是恢复线程的 中断状态。也就是说,在调用Thread.interrupted()方法之后,如果中断了此线程,则不会抛出中断异常,但是如果第二次调用Thread.interrupted()方法的话直接返回false,除非有第二次中断触发。

boolean isInterrupted = thread.isInterrupted();

boolean interrupted = Thread.interrupted();

yield()线程让步

只是让出CPU资源,停止执行当前线程的动作,当前线程的状态回到可运行状态。但是当前线程做出让步以后,下一个CPU资源会在哪个线程的身上,取决去调度器,调度器会在同等优先级的线程之间选择一个分配资源。

设置/获取优先级

优先级设置越大,获得CPU资源的可能性就越高。默认优先级使5,最小1,最大10。

thread.setPriority(10);

int priority = thread.getPriority();

join()等待线程结束

join()

join(long time)

上述两个方法都是等待线程结束,即在执行的地方先等待子线程运行结束再往下继续运行主线程。带参数的是最大等待时间,时间一到则不再等待子线程,主线程继续往下运行。一些场景就是主线程运行依赖于子线程的运行结果。

isAlive()

当前线程是否存活

守护线程

线程分为守护线程和用户线程。默认为用户线程。

setDaemon(boolean on)设置当前线程为守护线程

isDaemon()判断当前线程是否为守护线程。

线程的几种状态和转换

创建线程的三种方式

1、继承Thread类

Thread t = new MyThread();
t.start();

public static class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("my thread");
        }
}

这种方式是继承的方式,因为java是单继承,所以一般用的不多。

2、实现Runnable,重写run()方法

Thread t1 = new Thread(new RunnableThread());
t1.start();

public static class RunnableThread implements Runnable {
        @Override
        public void run() {
            System.out.println("runnable thread ... ");
        }
    }

这种实现接口的方式推荐使用

3、实现callable,重写call()方法

实现callable的方式,重写的call方法有返回值,这点和Runnable的接口有所不同,并且是交给FutureTask管理,通过future.get()可以获得call()方法的返回值。

注意的是,调用future.get()方法的时候,线程会等待call()方法执行完成,拿到返回值以后才会继续往下执行。

FutureTask futureTask = new FutureTask(new CallableThread());
futureTask.run();
String res = (String) futureTask.get();


public static class CallableThread implements Callable<String> {
        @Override
        public String call() throws Exception {
            System.out.println("callable thread ... ");
            TimeUnit.SECONDS.sleep(5);
            return "callable";
        }
    }

如果不需要返回值的话,用的最常见的就是第二种实现Runnable的方式,并且用lambda的方式,比如:

new Thread(()->{
            System.out.println("thread start ...");
        }).start();

线程池

集中管理线程,节省资源,线程复用,减少开销,便于维护。

Executors提供了固定的几种线程池的创建方式。

1、Executors.newFixedThreadPool(5);创建固定大小的线程池,参数是线程数量

2、Executors.newSingleThreadExecutor();创建只有一个线程数量的线程池
3、Executors.newCachedThreadPool();创建可以扩容的线程池,会一直增加线程数量,不建议使用。

上述3中线程池执行方法:

executorService.execute(()->{});传入Runnable对象

4、Executors.newScheduledThreadPool(5);创建有定时任务的线程池

这种线程池执行的方式scheduledExecutorService.schedule(()->{},5,TimeUnit.SECONDS);

第一个参数是Runnable对象,第二个参数是延迟时间,第三个参数是时间单位。

关闭线程池

shutdown(),等待所有的任务执行完毕关闭线程池。

shutdownNow(),等待正在执行的任务执行完毕后关闭线程池。

awaitTermination(10, TimeUnit.SECONDS),等待所有任务执行完毕,或者超时以后线程池关闭

isTerminated()线程池是否关闭

线程池的execute和submit方法区别

1、参数不同

execute()方法接收的是Runnable参数

submit接收的是Runnable或者Callable参数

2、返回值不同

execute()方法没有返回值

submit方法有返回值,返回的是Future类型,通过future.get()获得具体的结果。

注意:如果execute传入的是FutureTask类型的话,其实也是可以接收返回结果的。只不过submit替我们封装了FutureTask。

ExecutorService executorService = Executors.newFixedThreadPool(5);
Future<String> future = executorService.submit(() -> {
      return "123";
 });
 String s = future.get();
 System.out.println("s " + s);



 FutureTask<String> futureTask = new FutureTask(() -> {
      return "hello";
 });
 executorService.execute(futureTask);
 System.out.println("futuretask " + futureTask.get());

3、异常捕捉

execute()执行的子线程中的异常,只能子线程进行处理,主线程无法处理,即无法进行 程序的统一异常处理。

submit()中子线程的异常可以在主线程中进行统一异常处理,因为子线程出现异常的时候,并不会立即报错,在进行future.get()进行获取结果的时候才抛出异常,所以主线程可以进行统一异常处理。

自定义线程池

参数

corePoolSize:核心线程数

maximumPoolSize:最大线程数

keepAliveTime:非核心线程空闲状态存活时间

unit:存活时间单位

workQueue:工作/阻塞队列,

线程先加入到核心线程,核心线程数量达到以后,就会加入到阻塞队列,当阻塞队列满了以后加入非核心线程。

threadFactory:线程工厂

handler:拒绝策略

当线程已经超过了最大线程数加上阻塞队列的数量,就会触发拒绝策略

有四种拒绝策略:

AbortPolicy:拒绝新任务并抛出异常

DiscardPolicy:丢弃任务,不会抛异常

DiscardOldestPolicy:丢弃老的未处理的任务,新的任务加入队列

CallerRunsPolicy:自己处理新的任务,处理的逻辑在传入的Runnable方法中实现。

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,
                3,
                0,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(2),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );

        try {
            for (int i = 0; i < 50; i++) {
                Task task = new Task(i);
                threadPoolExecutor.submit(task);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPoolExecutor.shutdown();
        }
public static class Task implements Runnable {
        int i;

        public Task(int i) {
            this.i = i;
        }

        @Override
        public void run() {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("thread: "+Thread.currentThread().getName()+" ... ...");
        }
    }

  • 17
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值