并发编程1

一.如何创建一个线程?

  1.继承Thread类、实现Runnable接口。
  2.ExecutorService、Callable<T>、Future有返回结果的线程。

    public static void main(String[] args) {
        //创建线程池
        ExecutorService pool = Executors.newFixedThreadPool(5);
        //收集线程执行的结果
        List<Future<Integer>> list = new ArrayList<>();
        // 进行20次计算
        for (int i = 0; i < 20; i++) {
            MyCallable myCallable = new MyCallable(i);
            Future<Integer> future = pool.submit(myCallable);
            list.add(future);
        }
        //关闭线程池
        pool.shutdown();
        //输出计算结果
        for (Future<Integer> integerFuture : list) {
            try {
                System.out.println(integerFuture.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }

3.基于线程池的方式。

二:线程池

    Java中线程池的顶级接口是Executor,严格意义上Executor是一个线程执行器接口。真正的线程池接口是ExecutorService。

2.1 newCachedThreadPool

  创建一个可根据需要创建新线程的线程池,可以重用之前构造的线程。对于执行大量短期异步任务而言适合使用这个线程池。(调用execute时将优先重用空闲的之前构造好的线程,如果没有线程可以使用则创建一个新的线程执行任务。终止并从缓存中移除已经有60秒未被使用的线程)。所以长时间保持空线程池也不会造成资源浪费。

2.2 newFixedThreadPool

  创建一个可重用的固定线程数量的线程池,以共享无界队列的方式来运行这些线程。如果在线程池关闭前某个线程执行期间由于失败或其他原因终止,则重新创建一个线程代替他执行任务。在线程池被显式的关闭前,池中的线程一直存在。

2.3 newScheduledThreadPool

  创建一个线程池,他可安排在给定延时后运行命令或者循环执行命令。

 public static void main(String[] args) {
        ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(5);
        System.out.println("===============================");
//        scheduledPool.schedule(()->{
//            System.out.println("三秒后执行");
//        },3, TimeUnit.SECONDS);

        scheduledPool.scheduleAtFixedRate(()->{
            System.out.println("1秒后执行一次,之后每两秒执行一次");
        },1,2,TimeUnit.SECONDS);
//        scheduledPool.shutdown();
    }

2.4 newSingleThreadExecutor

    创建一个仅有一个线程的线程池。在线程池关闭之前线程一直存在。如果因为异常被中断则新建一个线程继续执行任务。

三、线程的生命周期

  new=>runnable=>running=>block=>running=>dead。wait

  线程被new关键字创建出来之后在执行start方法之前都是new状态。调用start方法后进入就绪(runnable)状态,与其他线程一起竞争cpu时间片。抢到cpu时间片后在cpu执行计算期间为running状态。

  阻塞状态(Blocked):

  阻塞状态是指线程因为某些原因放弃了cpu的使用权,重新回到runnable状态。进入阻塞状态的三种方式

  1.等待阻塞(o.wait->等待队列)

    在运行时的线程中调用o.wait方法,JVM会把该线程让如等待队列(waitting queue)中。

  2.同步阻塞(lock->锁池)

    运行(running)中的线程在获取对象的同步锁的时候,若该同步锁被别的线程占有,JVM会把该线程放入该对象同步锁的锁池(lock pool)中。

  3.其他阻塞(sleep/join)

    运行(running)中的线程执行Thread.sleep(long ms)或者t.join方法,或者发出I/O请求时。JVM会把该线程设置为阻塞状态。当sleep超时、join线程执行中止或超时、或I/O处理完毕时,线程重新进入就绪(runnable)状态。

  死亡状态(DEAD)

    线程以一下三种方式结束后就是死亡状态。

    1.正常结束。run()或call()方法执行结束。

    2.异常结束。线程抛出了一个未捕获的Exception或Error。

    3.调用stop。容易造成死锁不建议使用。

四、线程终止的四种方式

  1.线程正常运行结束。

  2.使用退出标志退出。可以搭配volatile关键字使用。

  3.Interrupt

    通过Interrupt终止线程有两种情况。

    1.被终止线程处于阻塞状态,阻塞中的线程调用interrupt()方法时会抛出InterruptException异常。通过代码捕获该异常之后brak跳出循环状态,从而终止线程。仅调用interrupt()方法没有捕获异常并跳出循环是不会终止线程的。

    2.被终止的线程没有处理阻塞状态则通过isInterrupt方法判断线程的中断标志来退出循环。当使用interrupt()方法时,中断标志会设置为true,和使用自定义的中断标志退出循环终止线程是一个道理。

    所以interrupt终止线程时需要isInterrupt方法和异常捕获一起使用。

  4.stop终止(线程不安全)不推荐使用。

posted @ 2021-11-16 15:29  democ  阅读(38)  评论(0)    收藏  举报