Java中并发工具包 - 下

执行器

执行器 文字说明

Java中并发工具包 - 下_第1张图片

执行器 - Callable与Futrue

Java中并发工具包 - 下_第2张图片

执行器 演示代码

package com.performer.demo1;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * 执行器 调用类
 * 演示目标
 *      两条线程,分别计算 1 - 100 之间数字的和,100 - 100000 之间数字的和
 * */
public class DemoCase {
    public static void main(String[] args) throws Exception {
        //创建ExecutorService对象,且2个线程的执行器
        ExecutorService es = Executors.newFixedThreadPool(2); 

        /**
         * Future 是callabel返回值对象
         * es.submit()方法就是callabel提交到线程中执行
         * */
        Future f1 = es.submit(new MC(1, 100)); // 提交任务
        Future f2 = es.submit(new MC(100, 100000)); 

        //通过get()方法 获取 call接口返回值
        System.out.println(f1.get() + ":" + f2.get());

        es.shutdown(); // 停止执行器
    }
}

// 1.实现 Callabel接口,并指定泛型的类型
class MC implements Callable // Callable泛型 是指定执行结果的返回类型
{
    // 2.创建共有变量
    private int begin, end;

    // 创建类的有参构造函数
    public MC(int begin, int end) {
        this.begin = begin;
        this.end = end;
    }

    /**
     * 3.实现了 callable接口方法 使用call方法计算 begin 到 end 数之间的和
     * call方法 返回线程执行结果
     * */
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = begin; i < end; i++) {
            sum += i;
        }
        return sum;
    }
}

执行器 - 运行结果

4950:704977754

执行器 使用总结

    执行器 API介绍 和使用步骤:
        1.在功能类中实现 Callable接口时,可以自定义线程的返回值类型。
        2.Callable接口 提供call方法,用于返回线程执行结果,
        3。在调用类中 首先创建 ExecutorService对象实例,使用ExecutorService 对象的 submit()方法 提交线程类。

        5.提交后,获取到Future对象实例,通过它的 get()方法获取线程执行的返回值。
        6.最后使用 ExecutorService线程管理对象的实例的shutdown() 方法,结束执行器。

ps:简单来说
    Callable接口 的作用:功能类 实现 Callable接口 就是相当于实现了一个 有返回值的线程。

    ExecutorService接口 的作用:控制线程执行和管理线程

    Future接口 的作用:获取 Callable接口实现类 的返回值

锁与原子操作

锁 与 原子操作 - API

Java中并发工具包 - 下_第3张图片

Java中并发工具包 - 下_第4张图片

锁 与 原操作 - 演示代码

package com.lock.demo1;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class DemoCase {
    public static void main(String[] args) {
        new MT().start();
        new MT().start();
        new MT().start();
        new MT().start();
        new MT().start();
    }
}

class Data {
    static int i = 0;
    static Lock lock = new ReentrantLock(); // 创建锁对象实例

    // static AtomicInteger ai = new AtomicInteger(0); //原子操作
    static void operate() {
        lock.lock(); // 开始加锁,保护资源同一时间只能被一个线程操作

        i++;
        System.out.println(i);

        lock.unlock(); // 关闭锁,因为后面没有资源需要保护
        // System.out.println(ai.incrementAndGet()); //原子操作 获取方法
    }
}

class MT extends Thread {
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            Data.operate();
        }
    }
}

不加锁 或者不使用原子操作的 运行结果

3
3
4
3
5
6
9
10
8
7
11
15
14
13
13

加锁 或者使用原子操作后的结果

1
2
3
4
5
6
7
8
9
10
11
12
13

锁 与 原子操作 使用总结

锁 与 原子操作 的相同点:
    1.保护共有资源的独立性,保证数据在同一时间只能被一条线程操作,从而保证数据的正确性和不重复性。

锁 与 原子操作 的不相同点:
    1。使用锁而获取到数据是有序的,反之原子操作是无序的。 

流编程

流编程 - 基本知识

Java中并发工具包 - 下_第5张图片

流编程 - 流的基本操作

Java中并发工具包 - 下_第6张图片

<>

流编程 - 演示代码

package com.stream;

import java.util.ArrayList;
import java.util.List;

public class DemoCase {
    public static void main(String[] args) {
        /**
         * 1.List:是一个有序的集合,可以包含重复的元素。提供了按索引访问的方式。它继承 Collection。
           2.List有两个重要的实现类:ArrayList 和 LinkedList
           3.ArrayList:我们可以将其看作是能够自动增长容量的数组。
           4.利用ArrayList的toArray()返回一个数组。
           5.Arrays.asList()返回一个列表。
         * */
        // 6.下面这样写法的好处:List是接口继承于Collection接口。ArrayList是List接口的实现类。相当于一个动态数组
        List list = new ArrayList<>();

        list.add("a");
        list.add("c");
        list.add("d");
        list.add("b");
        list.add("e");
        list.add("e");

        // ps:max是终端操作
//      //通过 stream方法获取流 ,通过max方法求流中最大的值,String::compareTo是筛选器
//      Optional max = list.stream().max(String::compareTo); 
//      
//      //使用get()方法回去返回值
//      System.out.println(max.get());

        //输出流中不重复的数据的数量
        System.out.println("不重复的数据有:"+list.stream().distinct().count()+"个");

        //下面使用中间操作
        /**
         * 运行步骤是获取流 -》进行排序,获得到了中间流 -》使用foreach 循环遍历 -》输出
         * */
        System.out.println("数据排序后的输出结果:");
        list.stream().sorted().forEach(e ->System.out.println(e));


    }
}

流编程 - 运行结果

不重复的数据有:5个
数据排序后的输出结果:
a
b
c
d
e
e

使用总结

    1.首先 stream 是在JDK8才有的,要使用必须保证自己的jdk版本是 1.82.eclispe的版本要在4.3以上,要支持1.8的语法。

    3.流的编程模型
        A.获取流:stream/parallelSteam
        B.操作:sort/max/min...
    4.流编程的作用:简化了我们处理数据,简单直接的达到了我们想要的结果。 

Fork/Join 框架

Fork/Join - API

Java中并发工具包 - 下_第7张图片

分而治之策略

Java中并发工具包 - 下_第8张图片

Fork/Join - 演示代码

package com.forkJoin;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;

/**
 * 任务目标: 计算 1-10w的和
 * 实现步骤:分为两部分计算。分别计算1-5w的和,5w-10w的和
 * */
public class DemoCase {

    public static void main(String[] args) {
        /*
         * ForkJoinPool: 管理ForkJoinTask的线程池
         * */
        ForkJoinPool forkJoinPool = new ForkJoinPool(); 

        //Future 表示结果返回值
        Future result = forkJoinPool.submit(new MTask(0, 1000)); //提交

        try {
            System.out.println("总计:"+result.get()); //输出
        } catch (InterruptedException | ExecutionException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        forkJoinPool.shutdown(); // 停止
    }
}

/**
 * 功能类:1.继承 RecursiveTask 抽象类,
 *      RecursiveTask 抽象类的特性:具有返回值
 * */
class MTask extends RecursiveTask
{

    //2.声明共有变量和常量
    private int begin,end; //开始 和 结束变量
    /**
     * 根据 java API分而治之策略 建议临界点定义在100-1000个操作中的位置
     * 
     * 限额1000,超过则继续划分成多个不超过1000的子任务
     * 
     * */
    static final int limit = 100; 
    //3.创建类的有参构造,传入参数
    public MTask(int begin,int end)
    {
        this.begin = begin;
        this.end   = end  ;
    }
    /**
     *4. 实现 RecursiveTask抽象类的 compute方法,
     * compute方法的作用是,任务执行后获取返回值
     * */
    @Override
    protected Long compute() {
        long sum = 0;
        //判断是否超过限额1000
        if(end - begin <= limit)
        {
            for (int i = begin; i < end; i++) {
                sum += i;
            }
        }else //超过限额,把一个任务划分2个子任务
        {
            int mid = (begin + end) / 2;  

            //拆分
            MTask left = new MTask(begin, mid);
            left.fork();

            MTask right= new MTask(mid + 1, end);
            right.fork();


            //分辨计算, 合并结果
            long lr = left.join();
            System.out.println(begin +"-"+mid+":"+lr);
            long rr = right.join();
            System.out.println(mid +"-"+end+":"+rr);

            sum = lr + rr ;
        }
        return sum;
    }
}

Fork/Join - 运行结果

251-313:17453
126-188:9703
0-62:1891
501-563:32953
63-125:5797
189-250:13359
314-375:20984
0-125:7688
564-625:36234
126-250:23062
251-375:38437
0-250:30750
376-438:25203
439-500:28609
751-813:48453
376-500:53812
501-625:69187
251-500:92249
814-875:51484
876-938:56203
0-500:122999
626-688:40703
939-1000:59109
751-875:99937
689-750:43859
876-1000:115312
626-750:84562
501-750:153749
751-1000:215249
501-1000:368998
总计:491997

Fork/Join - 使用总结

    Fork/Join 框架的特点的:
        1.分而治之的策略,让我们的程序可以最大、最优、最快的运行,特别适合大数据的计算,和统计。

你可能感兴趣的:(java)