一般来说,我是多线程的新手,所以我仍然不太了解它,所以我不明白为什么我的以下代码有问题。我正在尝试使用前1000个数字填充ArrayList,然后使用三个线程将所有数字相加。
public class Tst extends Thread {
private static int sum = 0;
private final int MOD = 3;
private final int compare;
private static final int LIMIT = 1000;
private static ArrayList<Integer> list = new ArrayList<Integer>();
public Tst(int compare){
this.compare=compare;
}
public synchronized void populate() throws InterruptedException{
for(int i=0; i<=Tst.LIMIT; i++){
if (i%this.MOD == this.compare){
list.add(i);
}
}
}
public synchronized void sum() throws InterruptedException{
for (Integer ger : list){
if (ger%MOD == this.compare){
sum+=ger;
}
}
}
@Override
public void run(){
try {
populate();
sum();
System.out.println(sum);
} catch (InterruptedException ex) {
Logger.getLogger(Tst.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static void main(String[] args) {
Tst tst1 = new Tst(0);
tst1.start();
Tst tst2 = new Tst(1);
tst2.start();
Tst tst3 = new Tst(2);
tst3.start();
}
}
理论上它应该显示“500.500”,但我得到的是
162241
328741
Exception in thread "Thread-0" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1042)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:996)
at tst.Tst.sum(Tst.java:38)
at tst.Tst.run(Tst.java:50)
BUILD SUCCESSFUL (total time: 2 seconds)
答案 0 :(得分:2)
之所以出现此问题,是因为您的方法是在“对象级别”中同步的,我的意思是,它使用的监视器锁是特定对象(tst1,tst2,tst3)的。换句话说,每个同步方法都使用不同的锁。 第一步,将同步方法更改为静态。
答案 1 :(得分:0)
当tst1的运行在for-each中计算总和时,则tst2的运行可能会增加列表的大小。因此,它引发并发修改异常。使用联接会有所帮助。
public static void main(String[] args) {
Tst tst1 = new Tst(0);
tst1.start();
tst1.join()
Tst tst2 = new Tst(1);
tst2.start();
tst1.join()
Tst tst3 = new Tst(2);
tst3.start();
}
答案 2 :(得分:0)
您误解了resource "google_container_cluster" "cluster" {
# ...
}
provider "helm" {
kubernetes {
host = "https://${google_container_cluster.cluster.endpoint}"
username = "${google_container_cluster.cluster.master_auth.0.username}"
password = "${google_container_cluster.cluster.master_auth.0.password}"
client_certificate = "${google_container_cluster.cluster.master_auth.0.client_certificate}"
client_key = "${google_container_cluster.cluster.master_auth.0.client_key}"
cluster_ca_certificate = "${google_container_cluster.cluster.master_auth.0.cluster_ca_certificate}"
}
}
方法的语义,您的情况下每个方法都使用不同的锁对象,请按以下方式进行操作:
synchronized
答案 3 :(得分:0)
您使用同步方法并没有实现您认为正在做的事情。编写代码的方式,保护“ sum”和“ populate”方法
从同一时间运行,但仅在同一线程实例上运行。这意味着对单个Tst
对象的“ sum”和“ populate”调用一次会发生一次,
但是允许在不同对象实例上同时调用“ sum”。
在方法上使用synchronized
等效于编写包装的方法
在整个方法主体中使用synchronized(this) { ... }
。通过创建三个不同的实例– tst1
,tst2
和tst3
–这种同步形式
不能保护跨对象实例。相反,它可以保证在单个对象上一次只能运行populate
或sum
中的一个;任何其他呼叫之一
这些方法(在同一对象实例上)将等到前一个方法完成。
看看Java语言规范中的8.4.3.6. synchronized Methods
有关更多详细信息。
您对static
的使用可能也没有按照您的想法做。您的代码还在Tst
线程类的所有实例(即sum
和list
)之间共享内容。由于这些定义为static
,
将有一个sum
和一个list
。您的代码中没有线程安全性,以防止同时更改其中任何一个。
例如,随着线程的更新
“ sum”(行:sum+=ger
),结果将是不确定的。也就是说,您每次运行它都可能会看到不同的结果。
具有多个线程和单个静态变量的意外行为的另一个示例是list
–随着时间的推移,它会不断增长,这可能导致并发问题。 The Javadoc说:
请注意,此实现未同步。如果有多个线程同时访问ArrayList实例,并且至少有一个线程在结构上修改了列表,则必须在外部进行同步。
修改包括添加值以及增加后备阵列存储。如果未指定起始大小– new ArrayList()
–根据您使用的JVM版本,它将默认为10或其他一些较小的数字。一旦一个线程尝试添加超过ArrayList容量的项目,它将触发自动调整大小。
每个ArrayList实例都有一个容量。容量是用于在列表中存储元素的数组的大小。它总是至少与列表大小一样大。将元素添加到ArrayList后,其容量会自动增长。除了添加元素具有固定的摊销时间成本这一事实外,没有指定增长策略的详细信息。