项目第三天

昨天写的文件扫描是单线程的,现在要使用多线程。

1、为什么要使用多线程?

为了提升扫描速度、为了学习多线程

2、Everything工具

只适合NTFS文件系统。文件有添加/修改/更新的时候,有记录日志,所以采用了扫描日志,而不是扫描树的方式进行。

如何通知主线程所有的任务已经完成?

一种解法:

构造任务的时候->传入带扫描文件夹->把它标记为dir

任务:

1、dir扫描出来——只需要扫描dir的孩子即可

2、扫描出所有的孩子中,还是文件夹的,封装成任务提交给线程池

哪个任务是最后一个?

每个人物都可能是最后一个完成的任务。

加入了打卡后可能会导致线程安全问题吗?

多线程安全问题:

public class Main {
    private static final int COUNT = 100000000;
    private static AtomicInteger ai = new AtomicInteger(0);

    public static void main(String[] args) throws InterruptedException {
        long b = System.currentTimeMillis();
        Thread adder = new Thread(() -> {
            for (int i = 0; i < COUNT; i++) {
                ai.getAndIncrement();  //+1
            }
        });
        adder.start();

        Thread suber = new Thread(() -> {
            for (int i = 0; i < COUNT; i++) {
                ai.getAndDecrement();  //-1
            }
        });
        suber.start();

        adder.join();
        suber.join();

        System.out.println(ai.get());
        long e = System.currentTimeMillis();
        System.out.println(e - b);
    }
}

用时不到2秒,

public class UseLock {
    private static final int COUNT = 100000000;
    private static int ai = 0;

    public static void main(String[] args) throws InterruptedException {
        long b =System.currentTimeMillis();
        Thread adder = new Thread(() -> {
            for (int i = 0; i < COUNT; i++) {
                synchronized (UseLock.class) {
                    ai++;
                }
            }
        });
        adder.start();

        Thread suber = new Thread(() -> {
            for (int i = 0; i < COUNT; i++) {
                synchronized (UseLock.class) {
                    ai--;
                }
            }
        });
        suber.start();

        adder.join();
        suber.join();

        System.out.println(ai);
        long e = System.currentTimeMillis();
        System.out.println(e-b);

    }
}

用时7秒。

 

最后一个完成任务的线程如何通知主线程  苏醒

1、主线程wait      最后一个完成任务的线程 notify

2、一个新的类:CountDownLatch

示例(比较复杂/会用就行):

public class Main {
    public static void main(String[] args) throws InterruptedException {
        final int N = 100;
        final long[][] arrays = new long[N][];  //n个数组

        Random r = new Random();
        for (int i = 0; i < N; i++) {
            arrays[i] = new long[100000];
            for (int j = 0; j < 100000; j++) {
                arrays[i][j] = r.nextInt();
            }
        }
        //数组初始化数据
        CountDownLatch startSignal = new CountDownLatch(1);  //开始信号
        CountDownLatch doneSignal = new CountDownLatch(N);   //结束信号

        Thread[] workers = new Thread[N];
        for (int i = 0; i < N; i++) {
            final long[] targetArray = arrays[i];
            Thread worker = new Thread(() -> {
                try {
                    //做一些准备工作
                    //还没有收到开始信号,等待
                    startSignal.await();
                    //收到开始信号,开始执行任务
                    Random random = new Random();
                    TimeUnit.SECONDS.sleep(random.nextInt(10) + 10);
                    Arrays.sort(targetArray);
                    Thread thread = Thread.currentThread();//返回当前线程对象的引用
                    System.out.printf("%s:以经完成任务\n",thread.getName());
                    //任务执行完成,还没有汇报
                    doneSignal.countDown();
                    //任务执行完成,并且已经汇报
                } catch (InterruptedException e) {
                    //有人让该线程中断
                    e.printStackTrace();
                }
            },String.format("工作线程-%02d",i));
            worker.start();
            workers[i] = worker;
        }
        //特工已经埋伏下去了,没有开始信号,特工不会开始执行任务

        //所以可以做一些准备工作
        startSignal.countDown();  //发出开始指令,表示各个特工可以开始执行任务了
        //能不能说,每个特工已经开始执行任务了?——不能

        TimeUnit.SECONDS.sleep(10); //这个期间可以做其他工作

        //等待所有特工都完成任务后,进行情况汇总
        doneSignal.await();  //一共有N个信号,所以,只有N个结束信号都收到时,表示所有任务都完成了
        //肯定所有任务都完成了
        //进行汇总工作
        for (int i = 0; i < N; i++) {
            //数组肯定都排好序了
            //肯定没有报错
            if (arrays[i][0] > arrays[i][1000000-1]) {
                System.out.println("错误");
            }
        }
    }
}

 

所以选用CountDownLatch。

 

package File_scan;

import java.io.File;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 联想
 * Date: 2021-01-20
 * Time: 15:03
 */
public class Main {
    private static ExecutorService threadPool = new ThreadPoolExecutor(
            10,10,
            0, TimeUnit.SECONDS,
            new LinkedBlockingDeque<>()
    );

    private static void scanDirectory(File root) throws InterruptedException {
        //打卡器 counter = new 打卡器();
        AtomicInteger counter = new AtomicInteger(0);
        CountDownLatch doneSignal = new CountDownLatch(1);
        ScanTask task = new ScanTask(root,threadPool,counter,doneSignal);

        //打卡
        counter.incrementAndGet();
        threadPool.execute(task);

        //TODO:想办法,在这里等,直到root整棵树扫描完成
        doneSignal.await();
        threadPool.shutdown();
    }

    public static void main(String[] args) throws InterruptedException {
        File root = new File("E:\\zhibo");

        scanDirectory(root);

        System.out.println("root整棵树 下的所有文件全部扫描完成");
    }
}

 

package File_scan;

import java.io.File;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 联想
 * Date: 2021-01-20
 * Time: 15:14
 */
public class ScanTask implements Runnable{
    private final File directory;
    private final ExecutorService threadPool;
    private final AtomicInteger counter;
    private final CountDownLatch doneSignal;
    public ScanTask(File directory, ExecutorService threadPool, AtomicInteger counter,CountDownLatch doneSignal) {
        this.directory = directory;
        this.threadPool = threadPool;
        this.counter = counter;
        this.doneSignal = doneSignal;
    }

    @Override
    public void run() {
        //打卡
        //counter.incrementAndGet();
        System.out.println(directory);
        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                System.out.println(file);
                if (file.isDirectory()) {
                    ScanTask task = new ScanTask(file,threadPool,counter,doneSignal);
                    //打卡
                    counter.incrementAndGet();
                    //提交给线程池
                    threadPool.execute(task);
                }
            }
        }

        //销卡
        if (counter.decrementAndGet() == 0) {
            //代表我是最后一个任务
            doneSignal.countDown(); //发送一个结束信号
        }
    }
}

这就是多线程扫描文件夹。

亮点:

1、多线程编程:那些环节可能出现线程问题?发现问题如何修复

2、Atomic类(原子类)

3、CountDownLatch

4、多线程可以提升速度,理论+实践

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值