使用线程修改对象

时间:2018-10-16 13:07:51

标签: java java-threads

我是线程新手。我想获得两个线程以将整数递增到某个值。因为int类型是不可变的,所以我切换到原子整数。我也试图将一个int包装到一个类上,但这也不起作用。我也尝试了static / volatile int,但没有用。我也尝试使用公平政策。主要问题是“ counterObj”没有正确地递增,即使被注入到两个线程中也仍然设置为0。

我预期的跑步行为:

thread       value
thread 0     0
thread 1     1
thread 0     2
...

到目前为止我写的是

import java.util.concurrent.atomic.AtomicInteger;

public class Application {
    public static void main(String[] args) {
        Application app = new Application();
        try {
            app.launch();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void launch() throws InterruptedException {
        int increments = 100;
        AtomicInteger counterObj = new AtomicInteger(0);
        CounterThread th1 = new CounterThread("1", counterObj, increments);
        CounterThread th2 = new CounterThread("2", counterObj, increments);
        th1.start();
        th2.start();


        System.out.println(counterObj.get());

    }


}

import java.util.concurrent.atomic.AtomicInteger;

public class CounterThread implements Runnable {
    private final String threadID;
    private AtomicInteger counterObj;
    private int bound;

    public CounterThread(String threadID, AtomicInteger counter, int bound) {
        this.threadID = threadID;
        this.counterObj = counter;
        this.bound = bound;
    }

    @Override
    public synchronized void run() {
        while (counterObj.get() < bound) {
            synchronized (this) {
                counterObj.incrementAndGet();
            }
        }
        System.out.println("Thread " + threadID + " finished");
    }


    public void start() throws InterruptedException {
        Thread thread = new Thread(this, threadID);
        thread.join();
        thread.start();
    }
}

干杯!

5 个答案:

答案 0 :(得分:3)

我认为您的程序在线程有机会做任何事情之前就已退出(可能是由于启动和联接的顺序所致。我会将线程的启动逻辑移到main(或launch)方法中。类似于以下内容

Thread thread1 = new Thread(new MyCounterRunnable("1", counterObj, increments));
Thread thread2 = new Thread(new MyCounterRunnable("2", counterObj, increments));

然后,在您的主目录中,您需要在启动线程之后调用join ,如下所示:

thread1.start(); // starts first thread.
thread2.start(); // starts second thread.

thread1.join(); // don't let main exit until thread 1 is done.
thread2.join(); // don't let main exit until thread 2 is done.

答案 1 :(得分:1)

您真正想要的是一次仅一个线程一次增加一个int。 int变量是您要在同步块中使用的资源,因此不同的线程可以一次将其递增一个。 可以单独使用syncrhonize来完成。 免责声明:我没有运行代码,因此可能会有一些错别字或异常要从Application类中删除。

public class Application {

  private int theVar = 0;
  private int increments = 100;

  public static void main(String[] args) {
    Application app = new Application();
    try {
        app.launch();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
  }

  public synchronized addOne(){
    this.theVar++;
  }

  private void launch() throws InterruptedException {
    Runnable counter1 = new Counter(this, increments), counter2 = new Counter(this, increments);
    Thread t1 = new Thread(counter1);
    Thread t2 = new Thread(counter2);
    t1.start();
    t2.start();
  }
}

柜台类

public class Counter implements Runnable{
  private Application app;
  int rounds = -1;

  public Counter(Application app, rounds){
    this.app = app;
    this.rounds = rounds;
  }
  public void run(){
    while(int i=0; i<rounds; i++){
        this.app.addOne();
    }
  }
}

答案 2 :(得分:1)

我已经对AtomicInteger进行了仔细检查,这似乎是您一直在尝试实现的目标。

import java.util.concurrent.atomic.AtomicInteger;

public class DualCounters{

    public static void main(String[] args) throws Exception{
        AtomicInteger i = new AtomicInteger(0);
        int bounds = 3;

        Thread a = new Thread(()->{
            int last = 0;
            while(i.get()<bounds){
                synchronized(i){
                    if(i.get()<bounds){
                        last = i.getAndIncrement();
                    }
                }
            }
            System.out.println("a last " + last);
        });

        Thread b = new Thread(()->{
            int last = 0;
            while(i.get()<bounds){
                synchronized(i){
                    if(i.get()<bounds){
                        last = i.getAndIncrement();
                    }
                }
            }
            System.out.println("b last " + last);
        });

        a.start();
        b.start();

        a.join();
        b.join();

        System.out.println(i.get() + " afterwards");

    }
}

仔细检查是Java中一个破损的概念,AtomicInteger提供了无需任何同步即可完成此操作的工具。

int a;
while((a = i.getAndIncrement())<bounds){
    ...
}

现在,while循环内的a永远不会大于范围。循环结束后,ia的值可以大于界限。

如果这是一个问题,那么总是有另一种方法getAndUpdate

while((a = i.getAndUpdate(i->i<bounds?i+1:i)<bounds){
    ...
} 

答案 3 :(得分:1)

AtomicInteger本身负责原子性,因此您不必使用synchronized -仅在您遵守规则并一次调用即可进行原子操作的情况下。

您无法执行此操作,因为先调用counterObj.get(),然后再根据结果counterObj.incrementAndGet()。您需要避免这种情况,因为您希望检查和更新成为同一原子工作块的一部分。

您可以通过以下方式接近:

while(counterObj.incrementAndGet() < bound) {} ;

但这总是至少增加一次,可能一次增加。

参与度稍高:

IntUnaryOperator incrementWithLimit = x -> 
   ( x < bound ? x + 1 : x );

while(counterObj.updateAndGet(incrementWithLimit) < bound) {};

也就是说,我们创建了一个仅在数字小于bound时递增数字的函数,然后告诉AtomicInteger应用该数字。

答案 4 :(得分:1)

您的代码有两个问题:

Thread.join方法仅在线程启动时有效,否则不执行任何操作。因此,您必须对代码重新排序,但是,如果只是在 start 之后移动 join 方法,则在通过调用 CounterThread.start 启动第一个线程时,主线程将等待,直到启动的线程完成为止,并在 Thread.join 方法中被阻塞,然后才继续启动第二个线程。一种解决方案是在 CounterThread 类中创建一个附加方法,该方法将在两个线程都启动之后被调用:

public void waitFinish() throws InterruptedException {
    thread.join();
}

已同步(此)在您调用 new CounterThread(...)时创建的 CounterThread 实例上进行同步,但是您有两个实例,因此每个实例将在一个不同的对象上进行同步。为了同步工作,您需要使用对象的公共实例,在这种情况下,您可以使用共享的 counterObj

仅保证 AtomicInteger 方法是线程安全的,因此,在检查是否已在同步块外部达到界限之后,在进入同步块时,该值可能已被另一个线程更改。因此,您需要在已同步的块中进行重新检查,或者先进行共享锁( counterObj )上的同步,然后再进行检查和递增。

        while (true) {
            synchronized (counterObj) {
                if (counterObj.get() < bound)
                    counterObj.incrementAndGet();
                else break;
            }
        }

请注意, AtomicInteger 类同步方法现在无济于事,但由于它是可变对象,因此有助于将其用作共享锁。如果改为使用 Integer (整数),则该实例是不可变的,因此增加实例后将创建一个新实例。所以现在,它的唯一功能就是保存整数结果的包装器。

将它们放在一起:

public class Application {
    public static void main(String[] args) {
        Application app = new Application();
        try {
            app.launch();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void launch() throws InterruptedException {
        int increments = 100;
        AtomicInteger counterObj = new AtomicInteger(0);
        CounterThread th1 = new CounterThread("1", counterObj, increments);
        CounterThread th2 = new CounterThread("2", counterObj, increments);
        th1.start();
        th2.start();
        th1.waitFinish();
        th2.waitFinish();

        System.out.println(counterObj.get());
    }
}

public class CounterThread implements Runnable {
    private final String threadID;
    private AtomicInteger counterObj;
    private int bound;
    private Thread thread;

    public CounterThread(String threadID, AtomicInteger counter, int bound) {
        this.threadID = threadID;
        this.counterObj = counter;
        this.bound = bound;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (counterObj) {
                if (counterObj.get() < bound)
                    counterObj.incrementAndGet();
                else break;
            }
        }
        System.out.println("Thread " + threadID + " finished");
    }


    public void start() throws InterruptedException {
        thread = new Thread(this, threadID);
        thread.start();
    }

    public void waitFinish() throws InterruptedException {
        thread.join();
    }
}