【问题标题】:Java: Sharing a variable across multiple threadsJava:跨多个线程共享变量
【发布时间】:2019-05-23 10:45:09
【问题描述】:

背景:我正在并行运行自动化测试。多个浏览器在相同数量的线程中启动,即 1 个浏览器是 1 个线程,使用 pom.xml 中的分叉。

pom.xml 中的以下插件创建与线程(fork)计数相同数量的Parallel**IT.class。
所有这些类都是同时并行执行的。因此,似乎每当我创建 volatile variable 或 AtomicInteger 时,每个线程都会创建自己的这些,因此跨多个线程共享变量的概念不起作用。

                <plugin>
                        <artifactId>maven-failsafe-plugin</artifactId>
                        <version>${maven.failsafe.plugin}</version>
                        <configuration>
                            <systemPropertyVariables>
                                <webdriver.base.url>${webdriver.base.url}</webdriver.base.url>
                                                                </systemPropertyVariables>
                            <argLine>
                                -javaagent:${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar
                            </argLine>
                            <forkCount>5</forkCount>
                            <reuseForks>true</reuseForks>
                            <includes>
                                <include>**/Parallel*IT.class</include>
                            </includes>
                            <perCoreThreadCount>true</perCoreThreadCount>
                            <properties>
                                <property>
                                    <name>listener</name>
            <value>ru.yandex.qatools.allure.junit.AllureRunListener</value>
                                </property>
                            </properties>
                        </configuration>
                        <executions>
                            <execution>
                                <goals>
                                    <goal>integration-test</goal>
                                    <goal>verify</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>


我只希望 1 个线程访问“准备测试数据”功能并将 flag 设置为 false,当其他线程将 flag 视为 false 时,它们不会尝试准备测试数据。

我正在按照教程https://www.youtube.com/watch?v=WH5UvQJizH0 使用volatile 变量来实现synchronization。也许我犯了一些错误,但所有线程都在打印System.out.println("Preparing test data");

尝试一:易失性和同步性

volatile boolean flag = false;

    public synchronized void setFlagTofalse(){
        System.out.println("Inside sync block");
          this.flag = true;
    }
    // works before class only once
    private EventHandler<TestRunStarted> prepareTestData = event -> {
            if(flag==false) {
                System.out.println("Preparing test data");              
                setFlagTofalse();
            }
    };

尝试 2:原子和同步

AtomicInteger flag = new AtomicInteger(0);

    private EventHandler<TestRunStarted> prepareTestData = event -> {
        if(flag.get()==0) {
            System.out.println("Preparing test data");
            value.incrementAndGet();
        }

【问题讨论】:

  • 请使用minimal reproducible example 更新您的问题。您的某些答案不起作用的各种 cmet 表明缺少某些上下文。
  • 缺少什么上下文?我会尝试更新,请至少提出您的猜测。
  • 首先,包含这些标志和方法的类。它们是如何被调用或启动的,等等。
  • 这在多线程和并行执行的情况下很明显,但还是让我把它放在问题描述中。
  • 这不是很明显,否则我不会问。这里的所有答案都假设这是一个将由所有线程共享的单个实例,但情况似乎并非如此。

标签: java multithreading synchronization atomic


【解决方案1】:

如果我是你,我会在访问标志变量时实现互斥锁。因此,在读取标志的值或更改其值之前,线程必须获取锁。这样他们就不会同时读取它或写入一个新值,而一个旧值直到被读取等等。

编辑:更多解释

@paul 解释一下锁的作用:它基本上是一个可以被线程抛来抛去的球。因此,如果您与一群人围成一圈,并且您拥有球权,那么轮到您谈论“X”了。然后,一旦你说完,你就把球放在圆圈的中间,它一直在那里,直到同一个人或其他人再次想要球并拿走它,或者等到它可用,然后可能会说“X”。在您的情况下,线程必须具有更改或读取变量标志的锁,所以您要做的是:

Mutex lock = new Mutex();
lock.aquire();
if ( flag == something ){
    //do something
}
mutex.release()

或者如果你正在改变标志。

lock.aquire();
flag = something;
lock.release();

如您所见,锁是在线程之间共享的。所以它是在管理线程的类中创建的,并传递给线程中启动的可运行对象或方法。

所以:

Mutex lock = new Mutex();
Runnable1 r1 = new Runnable1(lock);
Runnable2 r2 = new Runnable2(lock);
//use the lock in the methods of t1 and t2 that use your volitile var
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);

t1.start();
t2.start();

//the join wait for completion of the runnable method in your class.
t1.join();
t2.join();

祝你好运:)

【讨论】:

  • 如果线程没有得到数据,它会失败测试。我不想要那个。嘿,但是mutex lock 是我从未听说过的。如果您可以分享sn-p,那将是很好的学习。
  • @paul mutex 只不过是 lock... 您的同步对象已经是 Object 实例上的互斥体
  • @Eugene 说的是真的,但有时我更喜欢看到实际使用的锁的方法。
  • @BasvanderLinden 这是哪个api,这是import org.jruby.ext.thread.Mutex;?
  • @paul import java.util.concurrent.Semaphore,信号量就像互斥锁,但你可以决定有多少线程可以访问锁。在下面的链接中向下滚动到第 2 部分。阅读:mkyong.com/java/java-thread-mutex-and-semaphore-example
【解决方案2】:

如果你做一个测试 (flag.get()==0) 和一个动作,你有两个同步这两个东西。只需将整个 prepareTestData 方法标记为同步?

【讨论】:

    【解决方案3】:

    您同步了错误的方法。你可以试试这样的:

    volatile boolean flag = false;
    
    public void setFlagTofalse(){
        System.out.println("Inside sync block");
        this.flag = true;
    }
    // works before class only once
    private EventHandler<TestRunStarted> prepareTestData = event -> prepareData();
    
    private synchronized void prepareData() {
        if(!flag) {
            System.out.println("Preparing test data");
            setFlagTofalse();
        }
        //prepare data here
    }
    

    【讨论】:

    • @paul 如果这不起作用,那么您需要使用minimal reproducible example 更新您的代码,因为这意味着每个线程都有自己的flag 和/或自己的监视器来同步。
    • @MarkRotteveel 每次您发表评论时,它看起来都很有价值,但我无法理解。这次也没有得到。
    • @paul 这表明你真的需要复习你的并发和多线程知识。
    • 如果我知道我为什么要问?如果我想复习/改进/学习,我需要实施,当我们实施时,我们会卡住,当我们卡住时,我们会提出问题,当我们应该提出问题时,我们会想到可用的最佳论坛,所以我们在这里.
    【解决方案4】:

    “flag”和“lockObject”需要是“static”的,这样每个对象只有一个实例。虽然如果你有单例对象,那么你不必为“标志”使用“静态”,但“lockObject”应该是静态的。

    另外,使用“prepareTestData”中的“lockObject”进行同步。这表明java只有一个线程应该访问超过这一点的代码。一旦一个线程进入,其他线程将在进入之前等待该线程返回。

    private static Object lockObject = new Object();
    private static Boolean flag = Boolean.FALSE;
    
    private void prepareTestData() {
       synchronized(lockObject) { //This will keep other threads waiting
          if(!flag) {
              //prepare data
              flag = Boolean.TRUE;
           }
       }
    }
    

    希望这能回答你的问题。

    【讨论】:

    • 标志不需要是静态的。这将取决于问题中未显示的上下文。而且你绝对不应该使用Boolean 而不是boolean。
    • @punit 我是否需要告诉其他线程像Thread.sleep 一样等待,或者这些线程会自行等待?他们将如何摆脱等待?我已经尝试过 Sofo Gial 的答案,但没有奏效,可能是因为它没有静态的想法。让我尝试使用静态。
    • @MarkRotteveel 如答案中所述,如果每个线程都有自己的对象并调用准备好的数据,则标志必须是静态的。如果它是单身,那么它不是。没有收到你关于Boolean的声明
    • @paul java 负责线程的排队。如果同步不起作用,那么让我编辑我的答案以使用基于对象的锁定。
    • @PunitTiwan。是的,这不是队列或顺序,它是并行的,所有的都是并行运行的。谢谢,不胜感激。
    猜你喜欢
    • 2018-08-28
    • 1970-01-01
    • 2015-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-09
    • 2013-05-23
    相关资源
    最近更新 更多