繁体   English   中英

对Java并发结果感到困惑

[英]Confused about java concurrency results

因此,我正在通过尝试创建不良的并发示例,观察它们失败然后进行修复来研究Java并发性。

但是代码似乎从未中断过……我在这里错过了什么?

我有一个“共享对象”,是我的HotelWithMaximum实例。 据我所知,此类不是线程安全的:

package playground.concurrent;

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

public class HotelWithMaximum {

    private static final int MAXIMUM = 20;

    private List<String> visitors = new ArrayList<String>();

    public void register(IsVisitor visitor) {
        System.out.println("Registering : " + visitor.getId());
        System.out.println("Amount of visitors atm: " + visitors.size());

        if(visitors.size() < MAXIMUM) {
            //At some point, I do expect a thread to be interfering here where the condition is actually evaluated to
            //true, but some other thread interfered, adds another visitor, causing the previous thread to go over the limit
            System.out.println("REGISTERING ---------------------------------------------------------------------");
            //The interference might also happen here i guess...
            visitors.add(visitor.getId());
        }
        else{
            System.out.println("We cant register anymore, we have reached our limit! " + visitors.size());
        }
    }

    public int getAmountOfRegisteredVisitors() {
        return visitors.size();
    }

    public void printVisitors() {
        for(String visitor: visitors) {
            System.out.println(visitors.indexOf(visitor) + " - " + visitor);
        }
    }
}

访客是“ Runnables”(他们实现了我的接口IsVisitor,该接口从Runnable扩展),它们的实现方式如下:

package playground.concurrent.runnables;

import playground.concurrent.HotelWithMaximum;
import playground.concurrent.IsVisitor;

public class MaxHotelVisitor implements IsVisitor{

    private final String id;
    private final HotelWithMaximum hotel;

    public MaxHotelVisitor(String id, HotelWithMaximum hotel) {
        this.hotel = hotel;
        this.id = id;
    }

    public void run() {
        System.out.println(String.format("My name is %s and I am trying to register...", id));
        hotel.register(this);
    }

    public String getId() {
        return this.id;
    }

}

然后,为了使所有这些都在一个示例中运行,我将以下代码放在另一个类中:

public static void executeMaxHotelExample() {
        ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(6);
        HotelWithMaximum hotel = new HotelWithMaximum();

        for(int i = 0; i<100; i++) {
            executor.execute(new MaxHotelVisitor("MaxHotelVisitor-" + i, hotel));
        }

        executor.shutdown();

        try{
            boolean finished = executor.awaitTermination(30, TimeUnit.SECONDS);

            if(finished) {
                System.out.println("FINISHED WITH THE MAX HOTEL VISITORS EXAMPLE");
                hotel.printVisitors();
            }
        }
        catch(InterruptedException ie) {
            System.out.println("Something interrupted me....");
        }
    }

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

现在,我想念什么? 为什么这似乎永远不会失败? 该酒店课程不是线程安全的,对吗? 在此示例中,唯一使它“足够”线程安全的原因是(因为没有其他代码使hotel类中的线程不安全List混乱),我应该使注册方法“同步”,对吗? main方法中“ printVisitors()”方法的结果始终如下所示:

FINISHED WITH THE MAX HOTEL VISITORS EXAMPLE
0 - MaxHotelVisitor-0
1 - MaxHotelVisitor-6
2 - MaxHotelVisitor-7
3 - MaxHotelVisitor-8
4 - MaxHotelVisitor-9
5 - MaxHotelVisitor-10
6 - MaxHotelVisitor-11
7 - MaxHotelVisitor-12
8 - MaxHotelVisitor-13
9 - MaxHotelVisitor-14
10 - MaxHotelVisitor-15
11 - MaxHotelVisitor-16
12 - MaxHotelVisitor-17
13 - MaxHotelVisitor-18
14 - MaxHotelVisitor-19
15 - MaxHotelVisitor-20
16 - MaxHotelVisitor-21
17 - MaxHotelVisitor-22
18 - MaxHotelVisitor-23
19 - MaxHotelVisitor-24

列表中没有超过20位访问者...我觉得很奇怪...

ThreadPoolExecutor来自java.util.concurrent

java.util.concurrent包中的Java Concurrency Utilities框架是一个包含线程安全类型的库,该类型用于处理Java应用程序中的并发

因此ThreadPoolExecutor负责同步处理

请注意: ThreadPoolExecutor使用BlockingQueue来管理其作业队列java.util.concurrent.BlockingQueue是一个接口,要求所有实现都是线程安全的。

据我了解, java.util.concurrent的主要目标之一是,您可以在很大程度上进行操作,而无需使用Java的低级并发原语,包括synchronized, volatile, wait(), notify(), and notifyAll()很难使用。

还要注意, ThreadPoolExecutor实现了ExecutorService ,它不能保证所有实现都是线程安全的,但是根据文档http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html

在将Runnable或Callable任务提交给ExecutorService之前,线程中的操作发生在该任务执行任何操作之前 ,而该操作又发生在通过Future.get()检索结果之前。

happen-before解释:

Java™语言规范定义了内存操作(例如共享变量的读写)上的事前发生关系。 只有在写操作发生之前(在读操作之前),才能保证一个线程的写结果对另一线程的读取可见。

换句话说-通常不是线程安全的。

java.util.concurrent中所有类的方法及其子包将这些保证扩展到更高级别的同步

代码似乎从未中断过……我在这里想念什么?

Java语言规范为实现者提供了许多余地,可以最有效地利用任何给定的多处理器体系结构。

如果您遵守编写“安全的”多线程代码的规则,则可以保证正确实现的JVM将以您期望的方式运行您的程序。 但是,如果您违反规则,则不能保证您的程序行为不当。

通过测试查找并发错误是一个难题。 非“线程安全”程序可能会在一个平台上(即体系结构/ OS / JVM组合)在100%的时间内运行,它可能在其他平台上始终会失败,并且其在某些第三平台上的性能可能取决于其他平台进程正在一天中的某个时间或您只能猜测的其他变量上运行。

你是对的。

当您同时使用更多执行程序时,可以重现并发问题,例如Executors.newFixedThreadPool(100); 而不是6。那么更多线程将同时尝试它,则概率更高。 由于竞争条件/溢出只​​能发生一次,因此您将不得不运行主要时间更多次才能吸引更多访问者。

进一步,您需要在期望“干扰”的两个地方都添加一个Thread.yield() ,以使其更有可能发生。 如果执行时间很短/很快,则不会有任务切换,执行将是原子的(但不能保证)。

您也可以使用ThreadWeaver编写代码,该工具执行字节码操作(增加收益),以使此类问题更有可能发生。

通过这两个更改,我会不时在酒店中吸引30位以上的游客。 我有2x2 CPU。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM