在上一个问题中 - > my question here我收到了一个很好的解决方案(有效)来解决我的问题。 但我无法理解究竟是如何运作的。
因此,如果我有许多线程可以在此同步块上并发输入,并且根据java文档,此代码为:
synchronized(...){
//atomic for the operation inside
}
所以,我'问:
为什么这个操作是原子的:
for (int j = 0; j < column; j++) {
matrix[row][j] += 1;
}
而不是这一个:
System.out.println("begin print");
for (int i = 0; i < this.row; i++) {
System.out.println();
for (int j = 0; j < column; j++)
System.out.print(matrix[i][j]);
}
System.out.println();
System.out.println("end print");
我的全部功能是:
public void increaseRow(Integer row) {
synchronized (rows.get(row)) {
for (int j = 0; j < column; j++) {
matrix[row][j] += 1;
}
System.out.println("begin print");
for (int i = 0; i < this.row; i++) {
System.out.println();
for (int j = 0; j < column; j++)
System.out.print(matrix[i][j]);
}
System.out.println();
System.out.println("end print");
}
}
有人能给我一个有用的解释,我会非常感激。
答案 0 :(得分:2)
为什么不使用你的类对象:synchronized(this)
或者,更安全:synchronized(YourClassName.class)
或其他一些锁?
Object lock = new Object();
...
public void someMethod(){
synchronized(lock){...}
}
创建的每个Java对象(包括每个加载的类)都有一个关联的锁或监视器。将代码放在synchronized块中会使编译器附加指令以在执行代码之前获取指定对象的锁定,然后释放它(因为代码正常或异常完成)。在获取锁定并释放它之间,一个线程被称为“拥有”和“#34;锁。在线程A想要获取锁定时,如果线程B已经拥有它,则线程A必须等待线程B释放它。
(http://www.javamex.com/tutorials/synchronization_concurrency_synchronized1.shtml)
但是如果您的锁更改,而线程在同步块中使用此锁,则可能会发生另一个块可以使用此更改的锁进入同步块。 例如:
Object lock = new Object();
int value = 0;
public void increment(){
synchronized(lock){value++;}
}
public void printValue(){
synchronized(lock){System.out.println(value);}
}
的时间表:
线程1:
调用printValue()//获取锁定
线程2:
lock = new Object(); //锁定发生变化,现在是另一个对象
调用increment()//获取此新锁。旧的锁仍然由thread1保留
价值正在增加
threat1:
打印错误的值。
答案 1 :(得分:2)
正如评论中所述,System.out.println不是线程安全操作。
问题在于你锁定关键部分的方式。
synchronized (rows.get(row)) { }
此代码表示您锁定特定行,而不是整个表,所以如果你有N行,那意味着N锁存在,那么前N个线程就可以了同时运行并行填充System.out。
锁定一行可以提供更好的并行性:第2行的线程可以同时工作,就像第3行的线程一样。
另一种选择是为整个表部分设置一个锁。
Object lock = new Object();
...
public void someMethod(){
synchronized(lock){...}
}
在这种情况下,只有一个锁,并且只有一个Thread同时执行它,因此您可以从代码中同步调用System.out。
锁定表,减少并行性,因为减少了锁的数量,可用:在第2行上运行的线程需要等待第3行的Thread工作,才能释放锁。
线程安全,即同步保证仅影响函数,写入块,而不是外部调用函数,它不会使System.out原子操作。