为什么并发线程限制不能按预期工作?

时间:2016-09-20 18:32:29

标签: java multithreading concurrency

虽然有类似的问题,但我找不到任何类似的例子,就像我得到的那样。我非常感谢任何帮助,了解我的实施错误。

我正在尝试做什么:

我有一个Main class Driver,它可以实例化未知数量的线程。每个线程调用一个singleton类,该类应模拟“假”文件传输操作。 我遇到的问题是,无论并发请求的数量是多少,我都需要将并发传输限制为2次传输。

我尝试解决问题的方法是在Thread中添加每个新ConcurrentLinkedQueue并使用Executors.newFixedThreadPool(POOL_SIZE)管理它以将并发线程限制为2.对于每个交互 - 我使用pool.submit从池中轮询新线程。

我遇到的问题是我的输出是这样的: [Thread1],[Thread1,Thread2],[Thread1,Thread2,Thread3] ......

虽然它应该是: [Thread1,Thread2],[Thread3,Thread4]

为何限制在这里不起作用?

我的实施:

复印机 - 这是我的singleton课程。

public class Copier {

    private final int POOL_SIZE = 2;
    private static volatile Copier instance = null;
    private Queue<Reportable> threadQuere = new ConcurrentLinkedQueue();
    private static FileCopier fileCopier = new FileCopier();

    private Copier() {

    }

    public static Copier getInstance() {
        if (instance == null) {
            synchronized (Copier.class) {
                if (instance == null) {
                    instance = new Copier();
                }
            }
        }
        return instance;
    }

    public void fileTransfer(Reportable reportable) {
        threadQuere.add(reportable);

        ExecutorService pool = Executors.newFixedThreadPool(POOL_SIZE);
        for (int i=0; i < threadQuere.size(); i++) {
            System.out.println("This is the " + (i+1) + " thread");
            pool.submit(new CopyThread());
        }
        pool.shutdown();
        try {
            pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

CopyThread - 代表一个线程类

    public class CopyThread implements Reportable, Runnable {

    private static FileCopier fileCopier = new FileCopier();

    @Override
    public void report(String bitrate) {
        System.out.println(bitrate);
    }

    @Override
    public void run() {
        synchronized(fileCopier) {
            long startTime = System.nanoTime();
            long bytes = fileCopier.copyFile();
            long endTime = System.nanoTime();

            double duration = (double)(endTime - startTime) / 1000000000; // get in seconds
            double bytesInMegas = (double) bytes / 1000000;

            report(bytesInMegas + "MB were transferred in " + duration + " seconds");
        }
    }
}

驱动程序 - 我的main类我在哪里创建所有线程

    public class Driver {
    public static void main(String[] args) {
        Copier copier = Copier.getInstance();
        CopyThread copyThread1 = new CopyThread();
        CopyThread copyThread2 = new CopyThread();
        CopyThread copyThread3 = new CopyThread();
        CopyThread copyThread4 = new CopyThread();

        copier.fileTransfer(copyThread1);
        copier.fileTransfer(copyThread2);
        copier.fileTransfer(copyThread3);
        copier.fileTransfer(copyThread4);
        int q = 0;

    }
}

2 个答案:

答案 0 :(得分:2)

一个更简单的解决方案是Semaphore,有2个许可。

这确保“外部”线程也不能绕过限制,因为您的解决方案期望同时任务受到线程池大小的限制。

当单个工具就足够时,您的解决方案会使用多个并发工具。您的DCL单例也有点过时了。

答案 1 :(得分:2)

这里的一切都很好(虽然有点奇怪)。您在提交之前打印线程编号,您需要做的是将print放在run方法中,您将看到一切正常。打印件都会正常关闭,因为您使用打印的区域与Executors无关。您的代码存在更多问题,但我认为您只是为了测试/学习而做了所有这些,这就是为什么它就是这样。

在这种情况下,就像我说的那样,将print放在run方法中(你可以在CopyThread类中使用一些静态变量来计算线程)。您的输出将类似于2个关于线程编号(1和2)的打印,2个打印关于传输所花费的时间,然后打印关于线程3和4(我说可能,因为我们正在使用线程,不能确定任何事情) - 当你的fileTransfer提交4个runnables时,所有这一切都在第4步。您的单例已过时,因为它使用双重检查锁定,这在多线程计算机上是错误的,请检查:here。这不会破坏你的程序,所以以后会担心它。关于其他一切(奇怪的队列使用,fileTransfer方法制作新的线程池等)就像我说的那样,它可能用于学习,但如果不是 - 你的队列也可能被删除,你只是用它来计算这样的计数可以通过一些计数器变量来完成,你的fileTransfer方法应该只提交新的runnable到pool(这将是实例变量)来传输文件,而不是创建池并提交一些runnables,它有点像,直观。

编辑:检查一下,为了简单起见,我把全部放在Cat.java中,改变了一些我不得不改变的东西(我没有FileCopier类等等,但是你的问题答案就在这里):

import java.util.*;
import java.util.concurrent.*;
class Copier {
    private final int POOL_SIZE = 2;
    private static volatile Copier instance = null;
    private Copier() {

    }
    public static Copier getInstance() {
        if (instance == null) {
            synchronized (Copier.class) {
                if (instance == null) {
                    instance = new Copier();
                }
            }
        }
        return instance;
    }

    public void fileTransfer() {
        ExecutorService pool = Executors.newFixedThreadPool(POOL_SIZE);
        for (int i=0; i < 4; i++) {
            pool.submit(new CopyThread());
        }
        pool.shutdown();
        try {
            pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class CopyThread implements Runnable {
    private static int counter = 0;
    public void report(String bitrate) {
        System.out.println(bitrate);
    }
    Object obj = new Object();
    @Override
    public void run() {
        synchronized(obj) {
            System.out.println("This is the " + (++counter) + " thread");
            long startTime = System.nanoTime();
            long bytes = 0;
            for(int i=0; i<100000; i++)
                bytes+=1;
            long endTime = System.nanoTime();

            double duration = (double)(endTime - startTime) / 1000000000; // get in seconds
            double bytesInMegas = (double) bytes / 1000000;

            report(bytesInMegas + "MB were transferred in " + duration + " seconds");
        }
    }
}

public class Cat {
    public static void main(String[] args) {
        Copier copier = Copier.getInstance();
        copier.fileTransfer();
    }
}