多线程的实现

一.多线程的四种实现方式

1.通过继承Threa类

  • 实现步骤

    • 自定义一个类MyThread类,用来继承与Thread类

    • 在MyThread类中重写run()方法

    • 在测试类中创建MyThread类的对象

    • 使用对象调用start方法,启动线程。

  • 缺点

    • 这种方法是显式创建线程,有可能导致创建线程和销毁线程所花的时间以及系统资源开销过大,从而导致内存消耗完或者"过度切换"的问题

  • MyThread类

    package com.test.multithreading;
    ​
    /**
     * @author welcome
     */
    public  class MyThread extends Thread{
        public MyThread() {
        }
    ​
        public  MyThread(String name) {
            super(name);
        }
    ​
        @Override
        public  void run() {
            for (int i = 0; i < 50; i++) {
                System.out.println(this.getName()+" : "+i);
            }
        }
    }
  • 测试类

    package com.test.dy;
    ​
    import com.test.multithreading.MyThread;
    ​
    /**
     * @author welcome
     */
    public class Demo01 {
        public  static void main(String[] args) {
            MyThread t01=new MyThread();
            MyThread t02=new MyThread();
            MyThread t03=new MyThread();
    ​
            t01.start();
            t02.start();
            t03.start();
    ​
            t01.setName("线程1");
            t02.setName("线程2");
    ​
            Thread.currentThread().setName("主线程");
            for (int i = 0; i < 50; i++) {
                System.out.println(Thread.currentThread().getName()+" : "+i);
            }
        }
    }

2.实现Runnable接口

  • 实现步骤

    • 自定义一个MyRunnable类来实现Runnable接口

    • 在MyRunnable类中重写run()方法

    • 创建Thread对象,并把MyRunnable对象作为Tread类构造方法的参数传递进去

    • 使用对象调用start方法,启动线程。

  • 缺点

    • 这种方法是显式创建线程,有可能导致创建线程和销毁线程所花的时间以及系统资源开销过大,从而导致内存消耗完或者"过度切换"的问题

  • MyRunnable类

    package com.test.multithreading;
    ​
    /**
     * @author welcome
     */
    public class MyRunnable implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 50; i++) {
                System.out.println(Thread.currentThread().getName()+" : "+i);
            }
        }
    }
  • 测试类

    package com.test.dy;
    ​
    import com.test.multithreading.MyRunnable;
    ​
    /**
     * @author welcome
     */
    public class Demo02 {
        public static void main(String[] args) {
            MyRunnable myRun=new MyRunnable();
    ​
            Thread t01=new Thread(myRun,"线程1");
            Thread t02=new Thread(myRun,"线程2");
            Thread t03=new Thread(myRun,"线程3");
    ​
            t01.start();
            t02.start();
            t03.start();
    ​
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 20; i++) {
                        System.out.println(Thread.currentThread().getName()+" : "+i);
                    }
                }
            });
        }
    ​
    }

3.通过Callable和Future接口创建线程

  • 实现步骤

    • 自定义一个MyCallable类来实现Callable接口

    • 在MyCallable类中重写call()方法

    • 创建FutureTask,Thread对象,并把MyCallable对象作为FutureTask类构造方法的参数传递进去,把FutureTask对象传递给Thread对象。

    • 使用对象调用start方法,启动线程。

  • 优点

    • 可以借助FutureTask类,获取返回结果

    • 相比run()方法,可以有返回值

    • 方法可以抛出异常

    • 支持泛型的返回值

  • 缺点

    • 这种方法是显式创建线程,有可能导致创建线程和销毁线程所花的时间以及系统资源开销过大,从而导致内存消耗完或者"过度切换"的问题

    • 在获取t线程执行结果的时候,当前线程受阻塞,效率较低。

  • MyCallable类

    package com.test.multithreading;
    ​
    import java.util.concurrent.Callable;
    ​
    /**
     * @author welcome
     */
    public class MyCallable implements Callable {
        @Override
        public Object call() throws Exception {
            int sum=0;
            for (int i = 1; i <=100; i++) {
                if (i%2==0){
                    System.out.println(i);
                    sum+=i;
                }
            }
            return sum;
        }
    }
  • 测试类

    package com.test.dy;
    ​
    import com.test.multithreading.MyCallable;
    ​
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    ​
    /**
     * @author welcome
     */
    public class Demo03 {
        public static void main(String[] args) {
    ​
            MyCallable myCallable=new MyCallable();
    ​
            FutureTask futureTask=new FutureTask<>(myCallable);
    ​
            new Thread(futureTask).start();
    ​
            try {
                Object sum = futureTask.get();
                System.out.println("总合:" + sum);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
    ​
        }
    }

  • Future接口

    • 可以对具体Runnable、Callable任务的执行结果进行取消、查询是 否完成、获取结果等。

    • FutrueTask是Futrue接口的唯一的实现类。

    • FutureTask 同时实现了Runnable, Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。

4.通过线程池进行实现

  • Executors创建线程常用的4种方法

    • 公共类

      package com.test.multithreading.executorsandpool;
      import java.util.concurrent.atomic.AtomicInteger;
      ​
      /**
       * @author welcome
       */
      public class CreateThreadPollDemo {
          public static final int SLEEP_GAP=1000;
          public static class TargetTask implements Runnable{
              static AtomicInteger taskNo=new AtomicInteger(1);
              private String taskName;
              public TargetTask()
              {
                  taskName="task-"+taskNo;
                  taskNo.incrementAndGet();
              }
      ​
              @Override
              public void run() {
                  System.out.println(taskName+" is doing...");
                  try {
                      Thread.sleep(SLEEP_GAP);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  System.out.println(taskName+" end...");
              }
          }
      ​
      }

    • newSingleThreadExecutor创建“单线程化线程池”

      • 代码

        package com.test.dy.executorsandpooltest;
        ​
        import com.test.multithreading.executorsandpool.CreateThreadPollDemo;
        ​
        import java.util.concurrent.ExecutorService;
        import java.util.concurrent.Executors;
        ​
        /**
         * @author welcome
         */
        public class PoolTest1 {
            public static void main(String[] args) {
                ExecutorService pool=Executors.newSingleThreadExecutor();
                for(int i=0;i<3;i++)
                {
                    pool.execute(new CreateThreadPollDemo.TargetTask());
                    pool.submit(new CreateThreadPollDemo.TargetTask());
                }
                pool.shutdown();
        ​
            }
        }

      • 特点

        • 单线程化的线程池中的任务是按照提交的次序顺序执行的

        • 只有一个线程的线程池

        • 池中的唯一线程的存活时间是无限的

        • 当池中的唯一线程正繁忙时,新提交的任务实例会进入内部的阻塞队列中,并且其阻塞队列是无界的

        • 适用场景:任务按照提交次序,一个任务一个任务地逐个执行的场景

    • newFixedThreadPool创建“固定数量的线程池

      • 代码

        package com.test.dy.executorsandpooltest;
        ​
        import com.test.multithreading.executorsandpool.CreateThreadPollDemo;
        ​
        import java.util.concurrent.ExecutorService;
        import java.util.concurrent.Executors;
        ​
        /**
         * @author welcome
         */
        public class PoolTest2 {
            public static void main(String[] args) {
                ExecutorService pool= Executors.newFixedThreadPool(3);//最多有三个线程同时执行
                for (int i = 0; i < 5; i++) {
                    pool.execute(new CreateThreadPollDemo.TargetTask());
                    pool.submit(new CreateThreadPollDemo.TargetTask());
                }
                pool.shutdown();
            }
        }

         

      • 特点

        • 如果线程数没有达到“固定数量”,每次提交一个任务线程池内就创建一个新线程,直到线程达到线程池固定的数量

        • 线程池的大小一旦达到“固定数量”就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程

        • 在接收异步任务的执行目标实例时,如果池中的所有线程均在繁忙状态,新任务会进入阻塞队列中(无界的阻塞队列)

      • 适用场景:

        • 需要任务长期执行的场景

        • CPU密集型任务

      • 缺点:

        • 内部使用无界队列来存放排队任务,当大量任务超过线程池最大容量需要处理时,队列无限增大,使服务器资源迅速耗尽

    • newCachedThreadPool创建“可缓存线程池”

      • 代码

        package com.test.dy.executorsandpooltest;
        ​
        import com.test.multithreading.executorsandpool.CreateThreadPollDemo;
        ​
        import java.util.concurrent.ExecutorService;
        import java.util.concurrent.Executors;
        ​
        /**
         * @author welcome
         */
        public class PoolTest3 {
            public static void main(String[] args) {
                ExecutorService pool= Executors.newCachedThreadPool();
                for (int i = 0; i < 5; i++) {
                    pool.execute(new CreateThreadPollDemo.TargetTask());
                    pool.submit(new CreateThreadPollDemo.TargetTask());
                }
                pool.shutdown();
            }
        }
      • 特点

        • 在接收新的异步任务target执行目标实例时,如果池内所有线程繁忙,此线程池就会添加新线程来处理任务

        • 线程池不会对线程池大小进行限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小

        • 如果部分线程空闲,也就是存量线程的数量超过了处理任务数量,就会回收空闲(60秒不执行任务)线程

      • 适用场景:

        • 需要快速处理突发性强、耗时较短的任务场景,如Netty的NIO处理场景、REST API接口的瞬时削峰场景

      • 缺点

        • 线程池没有最大线程数量限制,如果大量的异步任务执行目标实例同时提交,可能会因创建线程过多而导致资源耗尽

    • newScheduledThreadPool创建“可调度线程池”

      • 代码

        package com.test.dy.executorsandpooltest;
        ​
        import com.test.multithreading.executorsandpool.CreateThreadPollDemo;
        ​
        import java.util.concurrent.Executors;
        import java.util.concurrent.ScheduledExecutorService;
        import java.util.concurrent.TimeUnit;
        ​
        /**
         * @author welcome
         */
        public class PoolTest4 {
            public static void main(String[] args) throws InterruptedException {
                ScheduledExecutorService pool= Executors.newScheduledThreadPool(2);
                for(int i=0;i<2;i++)
                {
                    pool.scheduleAtFixedRate(new CreateThreadPollDemo.TargetTask(), 0, 500, TimeUnit.MILLISECONDS);
                    //参数1: task任务
                    //参数2: 首次执行任务的延迟时间
                    //参数3: 周期性执行的时间
                    //参数4: 时间单位
        ​
                }
                Thread.sleep(3000);//主线程睡眠时间越长 周期次数越多
                pool.shutdown();
            }
        }
      • 特性

        • 延时性

        • 周期性

      • 缺点

        • 线程数量无上界,会导致创建大量的线程,从而导致OOM(内存耗尽)

    • newFixedThreadPoolnewSingleThreadExecutor阻塞队列无界,会堆积大量任务导致OOM(内存耗尽)
      newCachedThreadPoolnewScheduledThreadPool线程数量无上界,会导致创建大量的线程,从而导致OOM
  • ThreadPoolExecutor创建线程

    • 参数介绍

      corePoolSize核心线程池的大小。
      maximumPoolSize最大线程池的大小。
      keepAliveTime线程池维护线程所允许的空闲时间
      unit线程池维护线程所允许的空闲时间的单位
      workQueue用来暂时保存任务的工作队列。
      handler线程池对拒绝任务的处理策略
      threadFactory线程工厂
    • 代码

      import java.io.IOException;
      import java.util.concurrent.*;
      import java.util.concurrent.atomic.AtomicInteger;
      ​
      public class ThreadPoolExecutorTest {
      ​
          public static void main(String[] args) throws IOException {
              ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 10, TimeUnit.SECONDS,
                      new ArrayBlockingQueue<>(4), new NameTreadFactory(), new MyIgnorePolicy());
      ​
              // 预启动所有核心线程
              executor.prestartAllCoreThreads(); 
      ​
              for (int i = 1; i <= 10; i++) {
                  MyTask task = new MyTask(String.valueOf(i));
                  executor.execute(task);
              }
              
              //阻塞主线程
              System.in.read(); 
          }
      ​
          static class NameTreadFactory implements ThreadFactory {
      ​
              private final AtomicInteger mThreadNum = new AtomicInteger(1);
      ​
              @Override
              public Thread newThread(Runnable r) {
                  Thread t = new Thread(r, "my-thread-" + mThreadNum.getAndIncrement());
                  System.out.println(t.getName() + " has been created");
                  return t;
              }
          }
      ​
          public static class MyIgnorePolicy implements RejectedExecutionHandler {
      ​
              @Override
              public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
                  doLog(r, e);
              }
      ​
              private void doLog(Runnable r, ThreadPoolExecutor e) {
                  System.err.println(r.toString() + " rejected");
                  System.out.println("completedTaskCount: " + e.getCompletedTaskCount());
              }
          }
      ​
          static class MyTask implements Runnable {
              private String name;
      ​
              public MyTask(String name) {
                  this.name = name;
              }
      ​
              @Override
              public void run() {
                  try {
                      System.out.println(this.toString() + " is running!");
                      Thread.sleep(3000);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
      ​
              public String getName() {
                  return name;
              }
      ​
              @Override
              public String toString() {
                  return "MyTask [name=" + name + "]";
              }
          }
      }

    • 优点

      • 降低资源消耗,通过重复利用已创建的线程降低线程创建和销毁造成的消耗

      • 提高响应速度,当任务到达时任务不需要等到线程创建就能立即执行

      • 提高线程的可管理性,使用线程池进行统一的分配,调优和监控

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值