打卡面试题-day07(javaSE)

多线程和并发库

多线程基础

线程创建

(1)使用用Thread和接口Runnable实现

  1. 在Thread子类覆盖Thread的run方法
    方式一
        new Thread(){
            @Override
            public void run() {
                while (true){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();

方式二

  new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

总结:查看Thread类的run()方法的源代码,可以看到其实这两种方式否是在调用Thread对象的run()方法,如果Thread类的run方法没有被覆盖,并且为Thread对象设置了一个Runnable对象,该run方法会调用Runnable对象的run方法

(2)实现定时器Timer和TimerTask
Timer在实际开发中应用场景不多,一般来说都会用其第三方库来实现,但有时会在一些面试题中出现。例如以下:
模拟写出双重定时器

public class ThreadTest01 {
    //要求,使用定时器,间隔4秒执行一次,再间隔2秒执行一次,以此类推
    private static volatile int count = 0;
    static class TimerTastCus  extends TimerTask{
        @Override
        public void run() {
            count = (count+1)%2;
            System.out.println("Boob boom");
            new Timer().schedule(new TimerTastCus(),2000+2000*count);
        }
    }

    public static void main(String[] args) {

        Timer timer = new Timer();
        timer.schedule(new TimerTastCus(),2000+2000*count);

        while (true){
            System.out.println(new Date().getSeconds());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

线程互斥与同步

  1. 间接互相制约:一个系统中的多个线程必然共享某种系统资源,如共享CPU,共享IO设备,所谓间接相互制约即源于资源共享,打印机就是最好的例子,线程A正在使用打印机时,其他线程都要等待。
  2. 直接相互制约:主要是因为线程之间的合作,如有线程A将计算结果提供给线程B做进一步处理,那么线程B在线程A将数据到达之前处于阻塞状态。
    间接相互制约称为互斥,直接相互制约称为同步。
    要求:子线程运行10次后,主线程在运行5次,交替运行三遍
public class ThreadTest02 {
    public static void main(String[] args) {
        //要求子线程运行10次后,主线程再运行5次,交替运行三次
        final Bussiness bussiness = new Bussiness();
        //子线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 3; i++) {
                    bussiness.subMethod();
                }
            }
        }).start();
        //主线程
        for (int i = 0; i < 3; i++) {
            bussiness.mainMethod();
        }
    }
}

public class Bussiness {
    private boolean subFlag = true;

    public synchronized void mainMethod(){
        while (subFlag){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()
            +":main thread running loop count --"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        subFlag=true;
        notify();
    }
    public synchronized void subMethod(){
        while (!subFlag){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()
            +":sub thread running loop count --"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        subFlag=false;
        notify();
    }
}

线程局部变量ThreadLoacal

ThreadLoacl的作用和目的:用于实现线程内的数据共享,即对于相同的程序代码,多个模块在同一个线程中运行时共享一份数据,而在另外线程中运行时又共享另外一份数据。
每个线程调用全局ThreadLocal对象的set方法,在set方法中,首先根据当前线程获取当前线程的ThreadLocalMap对象,然后往这个map中插入一条记录,key其实是ThreadLocal对象,value是各自的set方法传进去的值。也就是每个线程其实都有一份自己独享的ThreadLocalMap对象,该对象的key是ThreadLocal对象,值是用户设置的具体值。在线程结束时可以调用ThreadLocal.remove()方法,这样会更快释放内存,不调用也可以,因为线程结束也可以自动释放相关的ThreadLocal变量。

ThreadLocal应用场景

订单处理的一系列操作:减少库存量,增加一条流水台账,修改总账,这几个操作要在同一个事务中完成,即同一个线程中进行处理。
银行转账包含一系列操作:把转出账户的余额减少,把转入账户的余额增加,这两个操作要在一个业务中完成,他们必须使用相同的数据库连接对象,转入和转出操作的代码分别是两个不同的账户对象的方法。

ThreadLocal的使用方式

在关联数据类中创建private staticThreadLocal

在下面的类中,私有静态 ThreadLocal 实例(serialNum)为调用该类的静态 SerialNum.get() 方法的每个线程维护了一个“序列号”,该方法将返回当前线程的序列号。(线程的序列号是在第一次调用 SerialNum.get() 时分配的,并在后续调用中不会更改。)

public class SerialNum {
    private static int nextSerialNum = 0;

    private static ThreadLocal serialNum = new ThreadLocal(){
        @Override
        protected synchronized Object initialValue() {
            return new Integer(nextSerialNum);
        }
    };
    public static int get(){
        return ((Integer)(serialNum.get())).intValue();
    }
}
public class ThreadContext {
    private String userId;
    private Long transactionId;
    private static  ThreadLocal threadLocal = new ThreadLocal(){
        @Override
        protected ThreadContext initialValue() {
            return new ThreadContext();
        }
    };

    public static ThreadContext get(){
        return (ThreadContext) threadLocal.get();
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public Long getTransactionId() {
        return transactionId;
    }

    public void setTransactionId(Long transactionId) {
        this.transactionId = transactionId;
    }

    public static ThreadLocal getThreadLocal() {
        return threadLocal;
    }

    public static void setThreadLocal(ThreadLocal threadLocal) {
        ThreadContext.threadLocal = threadLocal;
    }

    public String getUserId(){
        return userId;
    }
}
在Runnbale中创建ThreadLocal

在线程类内部创建ThreadLocal,基本步骤:

  1. 在多线程的类(如ThreadDemo类),创建一个ThreadLocal对象ThreadXxx,用来保存线程间需要间隔处理的对象xxx
  2. 在ThreadDemo类中,创建一个获取要间隔访问的数据的方法getXxx(),在方法中判断,若ThreadLocal对象为null,应该new()一个隔离访问类型的对象,并强制转换为要对应的类型
  3. 在ThreadDemo类的run方法中,通过调用getXxx()方法获取要操作的数据,这样可以保证每个线程对应一个数据对象,在任何时刻操作的是这个对象
public class ThreadLocalDemo implements Runnable {

    ThreadLocal<Student>studentThreadLocal = new ThreadLocal<Student>();
    @Override
    public void run() {
        String currentThreadName = Thread.currentThread().getName();
        System.out.println(currentThreadName+"is running...");
        Random random = new Random();
        int age = random.nextInt(100);
        System.out.println(currentThreadName+"is set age:"+age);
        Student student = getStudent();
        student.setAge(age);
        System.out.println(currentThreadName+"is first get age:"+student.getAge());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(currentThreadName+"is second get age:"+student.getAge());
    }

    private Student getStudent(){
       Student student =  studentThreadLocal.get();
       if (null == student){
           student = new Student();
           studentThreadLocal.set(student);
       }
       return student;
    }

    public static void main(String[] args) {
        ThreadLocalDemo tld = new ThreadLocalDemo();
        Thread t1 = new Thread("Thread A");
        Thread t2 = new Thread("Thread B");
        t1.start();
        t2.start();
    }
}
public class Student {
    int age;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

多线程共享数据

在java传统线程机制中的共享数据方式,大致分为两种情况

  1. 多个线程行为一致,共同操作一个数据源,也就是每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,卖票系统就可以这么做。
  2. 多个线程行为不一致,共同操作一个数据源。也就是每个线程执行的代码不同,这时候需要不同的Runnable对象。例如,银行存取款。

多个线程行为一致共同操作一个数据

如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如卖票系统。

public class TicketTest {
    /**
     * 共享数据
     */
    static class ShareData{
        private int num = 10;
        public synchronized void inc(){
            num++;
            System.out.println(Thread.currentThread().getName()+":invoke inc method num="+num);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 多线程
     */
    static class RunnableCusToInc implements Runnable{
        private ShareData shareData;
        public RunnableCusToInc(ShareData shareData){
            this.shareData=shareData;
        }
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                shareData.inc();
            }
        }
    }

    public static void main(String[] args) {
        ShareData shareData = new ShareData();
        for (int i = 0; i < 4; i++) {
            new Thread(new RunnableCusToInc(shareData),"Thread"+i).start();
        }
    }
}

多个线程行为不一致共同操作一个数据

如果多个线程执行的代码不同,这时候需要用不同的Runnable对象,有如下两种方式来实现这些Runnable对象之间的数据共享:

  1. 将共享数据封装在另外一个对象中,然后将这个对象逐一传递给Runnable对象,每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。
package test;

public class TicketTest {
    /**
     * 共享数据
     */
    static class ShareData{
        private int num = 10;
        public synchronized void inc(){
            num++;
            System.out.println(Thread.currentThread().getName()+":invoke inc method num="+num);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 多线程
     */
    static class RunnableCusToInc implements Runnable{
        private ShareData shareData;
        public RunnableCusToInc(ShareData shareData){
            this.shareData=shareData;
        }
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                shareData.inc();
            }
        }
    }

    public static void main(String[] args) {
        ShareData shareData = new ShareData();
        for (int i = 0; i < 4; i++) {
//            new Thread(new RunnableCusToInc(shareData),"Thread"+i).start();
            if (i%2 == 0){
                new Thread(new RunnableCusToInc(shareData),"Thread"+i).start();
            }else {
                new Thread(new RunnableCusToInc(shareData),"Thread"+i).start();
            }
        }
    }

    
}

  1. 将这些Runnable对象作为某一个类中的内部类,共享数据作为这个外部类中的成员变量,每个线程对共享数据的操作方法也分配外部类,以便实现对共享数据进行的各个操作的互斥和通信,作为内部类的各个Runnable对象用外部类的这些方法。
 /**
     * 共享数据
     */
    static class ShareData{
        private int num = 10;
        public synchronized void inc(){
            num++;
            System.out.println(Thread.currentThread().getName()+":invoke inc method num="+num);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public synchronized void dec(){
            num--;
            System.out.println(Thread.currentThread().getName()+":invoke inc method num="+num);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
 public static void main(String[] args) {
        ShareData shareData = new ShareData();
       /* for (int i = 0; i < 4; i++) {
//            new Thread(new RunnableCusToInc(shareData),"Thread"+i).start();
            if (i%2 == 0){
                new Thread(new RunnableCusToInc(shareData),"Thread"+i).start();
            }else {
                new Thread(new RunnableCusToInc(shareData),"Thread"+i).start();
            }
        }*/

        for (int i = 0; i < 4; i++) {
            if (i%2 == 0){
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int i1 = 0; i1 < 5; i1++) {
                            shareData.inc();
                        }
                    }
                },"Thread"+i).start();
            }else {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int i1 = 0; i1 < 5; i1++) {
                            shareData.inc();
                        }
                    }
                },"thread"+i).start();
            }
        }
    }

上面两种方式的组合:将共享数据封装在另外一个对象中,每个线程共享数据的操作方法也分配到那个对象身上完成,对象作为这个外部类中的成员变量或方法中的局部变量,每个线程的Runnable对象作为外部类中的成员内部类或局部内部类。
总之,要同步互斥的极端代码最好是分别放在几个独立的方法中,这些方法再放在同一个类中,这样比较容易实现他们之间的同步互斥和通信。

Java的并发库

java.util.concurrent包描述:
在并发编程中很常用的实用工具类。此包包括了几个小的,已标准化的可扩展框架,以及以及一些提供有用功能的类。此包下的一些组件,其包括:
执行程序(线程池)
并发队列
同步器
开发Collection

执行程序

Executors线程池工厂类
作用:限制系统中执行线程的数量,根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;减少了系统资源的浪费,

为什么使用线程池
减少创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务
可以根据系统的承受能力,调整线程池中工作线程的数目,防止因为消耗过多的内存,而把服务累趴下。

Executors详解
java里线程池的顶级接口,严格意义上讲Executor并不是一个线程池,而是一个执行线程的工具。真正的线程池接口是ExecutorService。ThreadPoolExecutor是Executors类的底层实现。
线程池的基本思想是一种对象池的思想,开辟一块内存空间,里面存放了众多的线程,池中线程执行调度有池管理器来处理。当有任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。
java5中并发库中,线程池创建方法

        //创建固定大小的线程池
        ExecutorService fPool = Executors.newFixedThreadPool(3);
        //创建缓存大小的线程池
        ExecutorService cPool = Executors.newCachedThreadPool();
        //创建单一的线程池
        ScheduledExecutorService sPool = Executors.newSingleThreadScheduledExecutor();
固定大小连接池
public class ThreadPoolTest01 {
    public static void main(String[] args) {
        //固定大小连接池
        //创建一个可重用固定线程数的线程池
        ExecutorService fPool = Executors.newFixedThreadPool(2);
        //创建一个使用单个worker线程的Executor,以无界队列方式来运行该线程
        //ScheduledExecutorService sPool = Executors.newSingleThreadScheduledExecutor();
        //可变连接池
        //创建一个使用单个worker线程的Executor,以无界队列方式来运行该线程
        //ExecutorService cPool = Executors.newCachedThreadPool();

        //延迟连接池
        //创建一个线程池,他可以安排给定延迟后运行命令或者定期地执行
        //ScheduledExecutorService Pool = Executors.newScheduledThreadPool(2);

        //创建实现了Runnable接口对象,Thread对象当然也要实现Runnable接口
        myThread t1 = new myThread();
        myThread t2 = new myThread();
        myThread t3 = new myThread();
        myThread t4 = new myThread();
        //将线程放入池中进行执行
        fPool.execute(t1);
        fPool.execute(t2);
        fPool.execute(t3);
        fPool.execute(t4);
        //关闭线程池
        fPool.shutdown();

      /*  sPool.execute(t1);
        sPool.execute(t2);
        sPool.execute(t3);
        sPool.execute(t4);
        sPool.shutdown();*/

       /* cPool.execute(t1);
        cPool.execute(t2);
        cPool.execute(t3);
        cPool.execute(t4);
        cPool.shutdown();*/

       /* Pool.execute(t1);
        Pool.execute(t2);
        Pool.execute(t3);
        Pool.execute(t4);
        Pool.shutdown();*/
    }

    /**
     * 创建自己线程
     */
    static class myThread extends Thread{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"正在执行。。。");
        }
    }
}

单任务连接池
 ScheduledExecutorService sPool = Executors.newSingleThreadScheduledExecutor();
         //创建实现了Runnable接口对象,Thread对象当然也要实现Runnable接口
        myThread t1 = new myThread();
        myThread t2 = new myThread();
        myThread t3 = new myThread();
        myThread t4 = new myThread();
         sPool.execute(t1);
        sPool.execute(t2);
        sPool.execute(t3);
        sPool.execute(t4);
        sPool.shutdown();
可变连接池
//可变连接池
        //创建一个使用单个worker线程的Executor,以无界队列方式来运行该线程
        ExecutorService cPool = Executors.newCachedThreadPool();
                //创建实现了Runnable接口对象,Thread对象当然也要实现Runnable接口
        myThread t1 = new myThread();
        myThread t2 = new myThread();
        myThread t3 = new myThread();
        myThread t4 = new myThread();
         cPool.execute(t1);
        cPool.execute(t2);
        cPool.execute(t3);
        cPool.execute(t4);
        cPool.shutdown();
延迟连接池
//延迟连接池
        //创建一个线程池,他可以安排给定延迟后运行命令或者定期地执行
        ScheduledExecutorService Pool = Executors.newScheduledThreadPool(2);
         //创建实现了Runnable接口对象,Thread对象当然也要实现Runnable接口
        myThread t1 = new myThread();
        myThread t2 = new myThread();
        myThread t3 = new myThread();
        myThread t4 = new myThread();
         Pool.execute(t1);
        Pool.execute(t2);
        Pool.execute(t3);
        Pool.execute(t4);
        Pool.shutdown();

ExecutorService执行器服务

java.util.concurrent.ExecutorService接口表示一个异步执行机制,使我们能够在后台执行任务,一次一个ExecutorService很类似于一个线程池,实际上,存在于java.util.concurrent包里的ExecutorService实现就是一个线程池实现
ExecutorService实例

public class ExecutorServiceTest {
    public static void main(String[] args) {
        //    线程工厂类创建出线程池
        ExecutorService fPool = Executors.newFixedThreadPool(10);
        //执行线程
        fPool.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("Asynchronus task...");
            }
        });
        //关闭线程池
        fPool.shutdown();
    }
}

首先使用newFixedThreadPool()工厂方法创建了一个ExecutorService,这里创建了一个十个线程执行任务的线程池。然后,讲一个Runnable接口的匿名实现类传递给execute()方法,这将导致ExecutorService中的某个线程执行Runnable。这里可以看成一个任务分派,
一个任务将一个任务委派给一个ExecutorService去异步执行
一旦该线程将任务委派给ExecutorService,该线程将继续它自己的执行,独立于该任务的执行。

ExecutorService实现

java.util.concurrent包提供了ExecutorService接口的以下实现类:
ThreadPoolExecutor
ScheduledThreadPoolExecutor

ExecutorService创建

ExecutorService的创建依赖于使用的具体实现,但是也可以使用Executors工厂类来创建,例如以下创建方式:

  //创建固定大小的线程池
        ExecutorService fPool = Executors.newFixedThreadPool(3);
        //创建缓存大小的线程池
        ExecutorService cPool = Executors.newCachedThreadPool();
        //创建单一的线程池
        ScheduledExecutorService sPool = Executors.newSingleThreadScheduledExecutor();
ExecutorService使用

有几种不同的方式来将任务委托给ExecutorService去执行:
execute(Runnable)
submit(Runnable)
submit(Callable)
invokeAny(…)
invokeAll(…)

execute(Runnable)

要求实现一个java.lang.Runnable对象,然后对它进行异步执行

public class ExecuteTest {
    public static void main(String[] args) {
        //从Executors中获取ExecutorService
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        //执行executorService
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("Asynchronize task");
            }
        });
        //关闭线程池
        executorService.shutdown();
    }
}
submit(Runnable)

submit(Runnable)方法也要要求一个Runnable实现类,但是它返回Future对象。这个Future对象可以用来检查Runnable是否已经执行完毕,以下是ExecutorService。submit()实例:

public class SubmitTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //从Executors中获取ExecutorService
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        Future future = executorService.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("Asynchronous task");
            }
        });
        //获得执行完run方法后的返回值
        future.get();
        executorService.shutdown();
    }
}
submit(Callable)

submit(Callable)方法类似于submit(Runnable)方法,除了所要求的参数类型之外,Callable实例除了它的call()方法能返回一个结果之外和一个Runnable很相像。Runable.run()不能够返回一个结果。Callable的结果可以通过submit(Callable)方法返回的Future对象进行获取,以下是ExecutorService Callable实例:

public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //从Executors中获得ExecutorService
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        Future future = executorService.submit(new Callable(){
            @Override
            public Object call() {
                System.out.println("Asynchronous Callable");
                return "Callable Result";
            }
        });
        System.out.println("future.get()="+future.get());
        executorService.shutdown();
    }
}

invokeAny()

invokeAny()方法要求一系列的Callable或者其子接口的实例对象。调用这个方法并不会返回一个Future,但是它返回一个Callable对象的结果。无法保证返回的是哪个Callable的结果-只能表明其中一个已执行结束。如果其中一个任务执行结束(或者抛出了一个异常),其他Callable将被取消,以下是实例:

public class InvokeAnyTest {
    public static void main(String[] args){
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        Set<Callable<String>> callables = new HashSet<>();

        callables.add(new Callable<String>(){
            @Override
            public String call() throws Exception{
                return "task 1";
            }
        });
        callables.add(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return "task 2";
            }
        });
        String result = null;
        try {
            result = executorService.invokeAny(callables);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("result="+result);
        executorService.shutdown();
    }
}

invokeAll()

invokeAll()方法将调用你在集合中传给ExecutorService的所有Callable对象。invokeAll()返回一系列的Future对象,通过他们可以获取每个Callable的执行结果。以下是实例:

public class InvokeAllTest {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        Set<Callable<String>> callables = new HashSet<>();
        callables.add(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return "task 1";
            }
        });

        callables.add(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return "task 2";
            }
        });
        List<Future<String>> futures = null;
        try {
            futures = executorService.invokeAll(callables);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (Future<String> future : futures) {
            System.out.println("future get="+future);
            try {
                future.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        executorService.shutdown();
    }
}

shutdown和shutdownNow区别

shutdown
将空闲的线程interrupt,shutdown()之前提交的任务可以继续执行直到结束
shutdownNow
是interrupt所有线程,因此大部分线程立刻被中断,之所以是大部分,而不是全部,是因为interrupt()方法能力有限。

ThreadPoolExecutor线程池执行者

java.util.current.ThreadPoolExecutor是ExecutorService接口的一个实现。ThreadPoolExecutor使用其内部池中的线程执行给定任务(Callable或者Runnable)
ThreadPoolExecutor包含的线程池能够包含不同数量的线程,池中线程的数量是由以下变量决定:
corePoolSize
maximumPoolSize
当一个任务委托给线程池时,如果池中线程数量低于corePoolSize,一个新的线程将被创建,即使池中可能尚有空闲线程,如果内部任务队列已满,而且至少corePoolSize正在运行,但是运行线程的数量低于maximumPoolSize,一个线程将被创建去执行该任务。

public class ThreadPoolExecutorTest {
    public static void main(String[] args) {
        int corePoolSize = 5;
        int maxPoolSize = 10;
        long keepAliveTime = 5000;
        ExecutorService threadPoolExecutor = new ThreadPoolExecutor(
                corePoolSize,
                maxPoolSize,
                TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>()
        );
        //构造方法参数列表详解
        corePoolSize--池中所保持的线程池,包括空闲线程。
        maximumPoolSize--池中允许的最大线程数。
        keepAliveTime--当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
        unit-keepAliveTime 参数的时间单位 。
        workQueue--执行前用于保持任务的队列,次队列仅保持由excute方法提交的Runable任务。
    }
}

ScheduledPoolExecutor定时线程池执行者

java.util.concurrent.ScheduledPoolExecutorService是一个ExecutorService,它能够将任务延后执行,或者间隔固定时间多次执行。任务由一个工作者线程异步执行,而不是由提交任务给ScheduedExecutorService的那个线程执行。

public class ScheduledPoolExecutorTest {
    public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
        scheduledExecutorService.schedule(new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                System.out.println("Executed");
                return "Called";
            }
        },5, TimeUnit.SECONDS);//5秒后执行

    }
}

首先一个内置10个线程的ScheduledExecutorService被创建。之后一个Callable接口的匿名类实例被创建然后传递给schedule()方法,后边的俩参数定义了Callable将在5秒之后执行。

ForkJoinPool合并和分叉(线程池)

java 7 引入,和ExecutorService相似,一点不同就是ForkJoinPool可以方便把任务分裂成几个更小的任务,分裂出来的任务将会交给ForkJoinPool。任务可以继续分割成更小的子任务,只要还能分割。
分叉
一个使用了分叉和合并原理的任务可以将自己分叉为更小的子任务,这些子任务可以并发执行。
合并
当一个任务将自己分割成若干子任务后,该任务将进入等待所有子任务的结束之中,一旦子任务执行结束,该任务可以把所有结果合并到同一个结果。
实现步骤

  1. 创建一个ForkJoinPool
    通过其构造子类创建一个ForkJoinPool。作为传递给ForkJoinPool构造子类的一个参数
  2. 提交任务到ForkJoinPool
    就像提交任务到ExecutorService那样,把任务提交到ForkJoinPool,两种提交类型的任务,一种是没有返回值,另一种是由返回值的,这两种分别有RecursiveAction和RecursiveTask表示。
  3. RecursiveAction
    是一种没有返回值的任务,只是做一些工作,比如写数据到磁盘,然后退出。例如:
public class MyRecursiveAction extends RecursiveAction {
    private long workLoad = 0;

    public MyRecursiveAction(long workLoad) {
        this.workLoad = workLoad;
    }

    @Override
    protected void compute() {
        //如果工作超过门槛,把任务分解成更小的任务
        if (this.workLoad>16){
            System.out.println("Splitting workLoad:"+this.workLoad);

            ArrayList<MyRecursiveAction> subtaks = new ArrayList<>();
            
            subtaks.addAll(createSubtaks());

            for (MyRecursiveAction subtak : subtaks) {
                subtak.fork();
            }
        }else {
            System.out.println("Doing workLoad myself:"+this.workLoad);
        }
    }

    private List<MyRecursiveAction> createSubtaks() {
        List<MyRecursiveAction> subtasks = new ArrayList<>();
        MyRecursiveAction subtask1 = new MyRecursiveAction(this.workLoad / 2);
        MyRecursiveAction subtask2 = new MyRecursiveAction(this.workLoad / 2);
        subtasks.add(subtask1);
        subtasks.add(subtask2);
        return subtasks;
    }

}

MyRecursiveAction将一个虚构的workLoad作为参数传给自己的构造子类,如果workLoad高于一个特定阈值,该工作将被分割为几个子工作,子工作继续分割。如果workLoad低于特定阈值,该工作将由MyRescursiveAction自己执行。可以规划一个MyRecursive的执行:

ForkJoinPool forkJoinPool = new ForkJoinPool(4);
MyRecursiveAction myRecursiveAction = new MyRecursiveAction(24);
forkJoinPool.invoke(myRecursiveAction);
  1. RecursiveTask
    RecursiveTask是一种返回结果的任务。它可以将自己的工作分割为若干更小任务,并将这些子任务的执行结果并到一个集体结果。可以由几个水平的分割和合并,以下是一个RecursiveTask实例:
public class MyRecursiveTask extends RecursiveTask<Long> {
    private long workLoad = 0;

    public MyRecursiveTask(long workLoad) {
        this.workLoad = workLoad;
    }
    protected Long compute() {
        //如果工作超过门槛,把任务分解成更小的任务
        if (this.workLoad>16){
            System.out.println("Splitting workLoad:"+this.workLoad);

            List<MyRecursiveTask> subtasks = new ArrayList<>();

            subtasks.addAll(createSubtaks());

            for (MyRecursiveTask subtask : subtasks) {
                subtask.fork();
            }
            long result = 0;
            for (MyRecursiveTask subtask : subtasks) {
                result += subtask.join();
            }
            return result;
        }else {
            System.out.println("Doing workLoad myself:"+this.workLoad);
            return workLoad*3;
        }
    }

    private List<MyRecursiveTask> createSubtaks() {
        List<MyRecursiveTask> subtasks = new ArrayList<>();
        MyRecursiveTask subtask1 = new MyRecursiveTask(this.workLoad / 2);
        MyRecursiveTask subtask2 = new MyRecursiveTask(this.workLoad / 2);
        subtasks.add(subtask1);
        subtasks.add(subtask2);
        return subtasks;
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

double_lifly

点喜欢就是最好的打赏!!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值