【课程学习】多线程基础应用

多线程基础应用

本文讲述简单的多线程应用:

1、多线程排序,即把数据存放在一维数组中,首先对数据进行分段,接着对每 一段数据采用经典排序算法实现排序,最后把各段数据进行合并排序。请完成程序编写。

2、多线程求数组最大值,即把数据存放在一维数组中,首先对数据进行分段, 接着对每一段数据求得最大值,最后把各段数据最大值进行比较从而得出整个 数组的最大值。请完成程序编写。

3、采用线程实现“生产者-消费者”编程的基础模型。

第一题:多线程排序

先试一下我们不用多线程的情况,以快速排序为例

package advance1;/*
 * @Author: Gnight
 * @Date: 2020/12/17 23:32
 * @Description:
    单线程的快速排序,太多数据栈会溢出
 */

import java.util.Arrays;

public class JavaDemo {

    public static int[] arr;

    public static void main(String[] args) {
        //随机生成数值
        arr = new int[100];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int) (Math.random() * 1000);
        }
        new JavaDemo().doSort(0, arr.length - 1);
        for (int element : arr) {
            System.out.println(element);
        }
    }

    public void doSort(int low, int high) {
        if (low < high) {
            int index = quickSort(low, high);//实际的排序流程
            doSort(low, index - 1);
            doSort(index + 1, high);
        }
    }

    public int quickSort(int i, int j) {
        int key = arr[i];//基准值
        while (i < j) {
            //找出第一个右边要交换的
            while (i < j && arr[j] >= key) j--;
            if (i < j) arr[i] = arr[j];
            //找出第一个左边要交换的
            while (i < j && arr[i] <= key) i++;
            if (i < j) arr[j] = arr[i];
        }
        // i== j的情况
        arr[i] = key;
        return i;
    }
}

那么如何实现多线程呢?

  1. 对数据分段,分别建立线程执行

  2. 合并每一段的结果

数据分段:

//根据我们设立的线程数来分段
for (int i = 0; i < threadNum; i++) {
    int[] temp = Arrays.copyOfRange(arr, i * arr.length / threadNum, 
       						(i + 1) * arr.length / threadNum);
    //theadNum就是线程数
}

快排线程:采用Callable接口,可以有返回值

package advance1;/*
 * @Author: Gnight
 * @Date: 2020/12/17 19:10
 * @Description:
    多线程实现快排
 */

import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;

//快排多线程
public class sortThread implements Callable<int[]> {

    private int[] arr;
    private int low;
    private int high;
    private CountDownLatch count;

    public sortThread(int[] arr, int low, int high, CountDownLatch count) {
        this.arr = arr;
        this.low = low;
        this.high = high;
        this.count = count;
    }

    public int[] call() throws Exception {
        System.out.println("线程 " + Thread.currentThread().getName() + " 开始");
        doSort(low, high);
        int[] res = new int[high - low + 1];
        int index = 0;
        for (int i = low; i < high + 1; i++) {
            res[index++] = arr[i];
        }
        try {
            return arr;
        } finally {
            count.countDown();
            System.out.println("线程 " + Thread.currentThread().getName() + " 结束");
        }
    }

    public void doSort(int low, int high) {
        if (low < high) {
            int index = quickSort(low, high);//实际的排序流程
            doSort(low, index - 1);
            doSort(index + 1, high);
        }
    }

    public int quickSort(int i, int j) {
        int key = arr[i];//基准值
        while (i < j) {
            //找出第一个右边要交换的
            while (i < j && arr[j] >= key) j--;
            if (i < j) arr[i] = arr[j];
            //找出第一个左边要交换的
            while (i < j && arr[i] <= key) i++;
            if (i < j) arr[j] = arr[i];
        }
        // i== j的情况
        arr[i] = key;
        return i;
    }
}

创建多线程,使用CountDownLatch保证前面都完成后再对数据段合并

try {
    CountDownLatch count = new CountDownLatch(threadNum);
    for (int i = 0; i < threadNum; i++) {
        int[] temp = Arrays.copyOfRange(arr, i * arr.length / threadNum, (i + 1) * arr.length / threadNum);
        Future<int[]> future = pool.submit(new sortThread(temp, 0,
                        temp.length - 1, count));
        res[i] = future.get();
    }
    /*
    使用CountDownLatch来保证前面线程都排序完,然后对排序完的升序数组合并
    */
    count.await();
    //这里排序亦可使用多线程
    int[] m1 = merge(res[0], res[1]);
    int[] m2 = merge(res[2], res[3]);
    arr = merge(m1, m2);
} catch (Exception e) {
    e.printStackTrace();
}

完整代码

package advance1;/*
 * @Author: Gnight
 * @Date: 2020/12/17 12:38
 * @Description:
    多线程实现排序:合并的时候也可以使用多线程
 */

import java.util.Arrays;
import java.util.concurrent.*;

public class SortDemo {

    public static void main(String[] args) {

        //线程数
        int threadNum = 4;
        System.out.println("创建线程数:" + threadNum);

        //创建线程池
        ThreadPoolExecutor pool = (ThreadPoolExecutor) Executors.newFixedThreadPool(threadNum);

        int[] arr = new int[5000000];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int) (Math.random() * 1000);
        }

//        display("排序前:", arr, ""); 太多了暂时不显示

        //最后的数组
        int[][] res = new int[threadNum][];

        //多线程排序
        try {
            CountDownLatch count = new CountDownLatch(threadNum);
            for (int i = 0; i < threadNum; i++) {
                int[] temp = Arrays.copyOfRange(arr, i * arr.length / threadNum, (i + 1) * arr.length / threadNum);
                Future<int[]> future = pool.submit(new sortThread(temp, 0,
                        temp.length - 1, count));
                res[i] = future.get();
            }
            count.await();
            //将结果归并,可改为多线程
            int[] m1 = merge(res[0], res[1]);
            int[] m2 = merge(res[2], res[3]);
            arr = merge(m1, m2);
            display("排序后:", arr, "");
        } catch (Exception e) {
            e.printStackTrace();
        }

        pool.shutdown();//关闭线程池

    }

    public static int[] merge(int[] a, int[] b) {
        int la = a.length, lb = b.length;
        int i = 0, j = 0, index = 0;
        int res[] = new int[la + lb];
        while (i < la && j < lb) {
            res[index++] = a[i] <= b[j] ? a[i++] : b[j++];
        }
        while (i < la) {
            res[index++] = a[i++];
        }
        while (j < lb) {
            res[index++] = b[j++];
        }
        return res;
    }

    public static void display(String prefix, int[] arr, String suffix) {
        System.out.print(prefix);
        for (int i = 0; i < arr.length; i++) {
            if (i % 20 == 0) System.out.println();
            System.out.print(arr[i] + " ");
        }
        System.out.println(suffix);
    }

}

第二题:多线程求数组最大值

同样分组完后在对数据处理,由于差不多所以直接看代码吧

具体线程实现

package advance2;/*
 * @Author: Gnight
 * @Date: 2020/12/17 22:51
 * @Description:

 */

import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;

import static java.lang.Character.MIN_VALUE;

public class getMaxThread implements Callable<Integer> {

    private int[] arr;
    private CountDownLatch count;

    public getMaxThread(int[] arr, CountDownLatch count) {
        this.arr = arr;
        this.count = count;
    }

    @Override
    public Integer call() throws Exception {
        if (arr == null || arr.length <= 0) return null;
        int max = MIN_VALUE;
        for (int i = 0; i < arr.length; i++) {
            if (max < arr[i]) max = arr[i];
        }
        count.countDown();
        return max;
    }
}

测试类

package advance2;/*
 * @Author: Gnight
 * @Date: 2020/12/17 22:49
 * @Description:
    求最大值(多线程)
 */

import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.*;

public class getMaxDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //线程数
        int threadNum = 4;
        System.out.println("创建线程数:" + threadNum);

        //创建线程池
        ThreadPoolExecutor pool = (ThreadPoolExecutor) Executors.newFixedThreadPool(threadNum);

        int[] arr = new int[20];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int) (Math.random() * 1000);
        }
        //初始化后的数组为
        display("初始化:", arr);

        ArrayList<Integer> list = new ArrayList<Integer>();
        CountDownLatch count = new CountDownLatch(threadNum);
        for (int i = 0; i < threadNum; i++) {
            int[] subArray = Arrays.copyOfRange(arr, i * arr.length / threadNum, (i + 1) * arr.length / threadNum);
            Future<Integer> future = pool.submit(new getMaxThread(subArray, count));
            list.add(future.get());
        }
        count.await();//等待都搞定
        int max = list.get(0);
        for (int i = 1; i < list.size(); i++) {
            if (max < list.get(i)) max = list.get(i);
        }
        System.out.println("\n最大值为:" + max);
        pool.shutdown();
    }

    public static void display(String prefix, int[] arr) {
        System.out.print(prefix);
        for (int i = 0; i < arr.length; i++) {
            if (i % 20 == 0) System.out.println();
            System.out.print(arr[i] + " ");
        }
    }
}

第三题:生产者&消费者

这个要注意的就是线程安全,可以采用阻塞队列来实现

定义产品类

package advance3;/*
 * @Author: Gnight
 * @Date: 2020/12/17 23:05
 * @Description:
    产品
 */

public class Product {
    private String name;

    public Product(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Product{" +
                "name='" + name + '\'' +
                '}';
    }
}

主要类

package advance3;/*
 * @Author: Gnight
 * @Date: 2020/12/17 23:01
 * @Description:
    生产者与消费者模型
 */

import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;

public class JavaDemo {
    public static void main(String[] args) {
        //线程安全的队列
        LinkedBlockingQueue<Product> queue = new LinkedBlockingQueue<>();
        //生成着
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                while (true) {
                    Product product = new Product(UUID.randomUUID().toString());
                    queue.add(product);
                    System.out.println(Thread.currentThread().getName() + "  生产了新产品:" + product.toString());
                    try {
                        Thread.currentThread().sleep(1000);//生产完休息一下
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "线程 " + String.valueOf(i)).start();
        }
        //消费者
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                while (true) {
                    Product product = queue.poll();
                    if (product != null)
                        System.out.println(Thread.currentThread().getName() + "  消费了产品:" + product.toString());
                    try {
                        Thread.currentThread().sleep(2000);//消费完休息一下
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "线程 " + String.valueOf(i)).start();
        }
    }
}

你可能感兴趣的:(笔记,多线程,java,算法,队列,线程安全)