java – 多线程总是比单线程产生更好的性能?

前端之家收集整理的这篇文章主要介绍了java – 多线程总是比单线程产生更好的性能?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我知道答案是否定的,这里是一个例子 Why single thread is faster than multithreading in Java?.

因此,当处理线程中的任务是微不足道的时候,创建线程的成本会比分配任务创造更多的开销.这是一个单线程比多线程快的情况.

问题

>有更多的情况下单线程将比多线程快吗?
>我们什么时候决定放弃多线程,只使用一个线程来完成我们的目标?

虽然这个问题是java标签,但也欢迎讨论超越Java.
如果我们可以有一个小例子来解释答案,这将是巨大的.

解决方法

这是一个非常好的问题,关于线程及其与实际工作的链接,意味着可用的物理cpu及其内核和超线程.

>如果您的cpu有多个核心可用,多线程可能会让您并行执行此操作.所以在一个理想的世界,例如如果您的cpu有4个核心可用,并且您的算法工作真正并行,那么使用4个线程可能会增加一些素数的4倍.
>如果您启动更多的线程作为核心可用,您的操作系统的线程管理将花费越来越多的时间在线程交换机,并以您的效率,使用您的cpu变得更糟.
>如果编译器,cpu缓存和/或运行时实现了您运行多个线程,则访问内存中的相同数据区域将以不同的优化模式运行:只要编译/运行时才确定只有一个线程访问数据,可以避免将数据经常写入外部RAM,并可能有效地使用cpu的L1缓存.如果没有:必须激活信号量,并且更频繁地将缓存的数据从L1 / L2缓存刷新到RAM.

所以我从高度parrallel多线程中学到的经验是:

>如果可能,使用单线程,无共享进程来提高效率
>如果需要线程,尽可能地解耦共享数据访问
>如果可能,不要尝试分配比可用核心更多的加载工作线程

这里有一个小程序(javafx)来玩.它:

>分配一个100.000.000大小的字节数组,填充随机字节
>提供一个方法,计数在这个数组中设置的位数
>该方法允许计数每个第n个字节位
count(0,1)将计数所有字节位
> count(0,4)将计数0′,4′,8’字节位,允许并行交错计数

使用MacPro(4核)导致:

>运行一个线程,count(0,1)需要1326ms计数所有399993625位
>运行两个线程,并行计数(0,2)和计数(1,2)需要920ms
>运行四个线程,需要618ms
>运行八个线程,需要631ms

改变计数方式,例如增加一个共同的整数(AtomicInteger或synchronized)会大大改变许多线程的性能.

public class MulithreadingEffects extends Application {
    static class ParallelProgressBar extends ProgressBar {
        AtomicInteger myDoneCount = new AtomicInteger();
        int           myTotalCount;
        Timeline      myWhatcher = new Timeline(new KeyFrame(Duration.millis(10),e -> update()));
        BooleanProperty running = new SimpleBooleanProperty(false);

        public void update() {
            setProgress(1.0*myDoneCount.get()/myTotalCount);
            if (myDoneCount.get() >= myTotalCount) {
                myWhatcher.stop();
                myTotalCount = 0;
                running.set(false);
            }
        }

        public boolean isRunning() { return myTotalCount > 0; }
        public BooleanProperty runningProperty() { return running; }

        public void start(int totalCount) {
            myDoneCount.set(0);
            myTotalCount = totalCount;
            setProgress(0.0);
            myWhatcher.setCycleCount(Timeline.INDEFINITE);
            myWhatcher.play();
            running.set(true);
        }

        public void add(int n) {
            myDoneCount.addAndGet(n);
        }
    }

    int mySize = 100000000;
    byte[] inData = new byte[mySize];
    ParallelProgressBar globalProgressBar = new ParallelProgressBar();
    BooleanProperty iamReady = new SimpleBooleanProperty(false);
    AtomicInteger myCounter = new AtomicInteger(0);

    void count(int start,int step) {
        new Thread(""+start){
            public void run() {
                int count = 0;
                int loops = 0;
                for (int i = start; i < mySize; i+=step) {
                    for (int m = 0x80; m > 0; m >>=1) {
                        if ((inData[i] & m) > 0) count++;
                    }
                    if (loops++ > 99) {
                        globalProgressBar.add(loops);
                        loops = 0;
                    }
                }
                myCounter.addAndGet(count);
                globalProgressBar.add(loops);
            }
        }.start();
    }

    void pcount(Label result,int n) {
        result.setText("("+n+")");
        globalProgressBar.start(mySize);
        long start = System.currentTimeMillis();
        myCounter.set(0);
        globalProgressBar.runningProperty().addListener((p,o,v) -> {
            if (!v) {
                long ms = System.currentTimeMillis()-start;
                result.setText(""+ms+" ms ("+myCounter.get()+")");
            }
        });
        for (int t = 0; t < n; t++) count(t,n);
    }

    void testParallel(VBox Box) {
        HBox hBox = new HBox();

        Label result = new Label("-");
        for (int i : new int[]{1,2,4,8}) {
            Button run = new Button(""+i);
            run.setOnAction( e -> {
                if (globalProgressBar.isRunning()) return;
                pcount(result,i);
            });
            hBox.getChildren().add(run);
        }

        hBox.getChildren().addAll(result);
        Box.getChildren().addAll(globalProgressBar,hBox);
    }


    @Override
    public void start(Stage primaryStage) throws Exception {        
        primaryStage.setTitle("ProgressBar's");

        globalProgressBar.start(mySize);
        new Thread("Prepare"){
            public void run() {
                iamReady.set(false);
                Random random = new Random();
                random.setSeed(4711);
                for (int i = 0; i < mySize; i++) {
                    inData[i] = (byte)random.nextInt(256);
                    globalProgressBar.add(1);
                }
                iamReady.set(true);
            }
        }.start();

        VBox Box = new VBox();
        Scene scene = new Scene(Box,400,80,Color.WHITE);
        primaryStage.setScene(scene);

        testParallel(Box);
        GUIHelper.allowImageDrag(Box);

        primaryStage.show();   
    }

    public static void main(String[] args) { launch(args); }
}

猜你在找的Java相关文章