我是线程新手。我想获得两个线程以将整数递增到某个值。因为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();
}
}
干杯!
答案 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永远不会大于范围。循环结束后,i
和a
的值可以大于界限。
如果这是一个问题,那么总是有另一种方法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();
}
}