我是多线程的新手,我编写了这段代码,通过同时运行线程增量并打印变量来打印数字1-10000。
这是我正在使用的代码:
package threadtest;
public class Main{
static int i=0;
static Object lock=new Object();
private static class Incrementer extends Thread{
@Override
public void run(){
while (true){
synchronized(lock){
if (i>=10000)
break;
i++;
System.out.println(i);
}
}
}
}
public static void main(String[] args) {
new Incrementer().start();
new Incrementer().start();
new Incrementer().start();
new Incrementer().start();
new Incrementer().start();
new Incrementer().start();
}
}
这很有效 - 我编写了一个测试程序来检查输出,打印的数字正好是1-10000。
我的问题是:我听说synchronized
只是语法糖。但是,如果不使用它,我似乎无法取得成功。我错过了什么?
答案 0 :(得分:14)
synchronized
绝不是任何语法糖。如果不使用synchronized
关键字,就无法在Java中使用锁。
如果Java中的锁中存在“语法糖”,那么synchronized
可以同时应用于块(如上所述)和整个方法。以下两种方法在语义上大致相同:
synchronized void method1() {
// ... do stuff ...
}
void method2() {
synchronized(this) {
// ... do stuff ...
}
}
那你为什么要做第二个版本而不是第一个呢?
this
以外的对象进行锁定,从而为您提供更灵活的锁定语义。答案 1 :(得分:1)
听起来你的消息来源是错的。在编写线程安全代码时,syncrhonized
关键字非常重要 - 并且可以正确使用。听起来你自己的实验证明了这一点。
有关Java中同步的更多信息:
答案 2 :(得分:1)
实际上,从Java 5开始,你(正式)在java.util.concurrent中有一套替代工具。有关详细信息,请参阅here。正如文章中详述的那样,Java语言级别提供的监视器锁定模型具有许多重要的局限性,并且当存在一组复杂的相互依赖的对象和锁定关系使得实时锁定成为可能时,可能难以推断。 java.util.concurrent库提供了锁定语义,这对于在POSIX类系统中有过经验的程序员来说可能更为熟悉
答案 3 :(得分:1)
当然,“同步”只是句法糖 - 极端有用的句法糖。
如果你想要无糖的java程序,你应该直接在java字节码中编写 monitorenter , monitorexit , lock ,以及<{3}}
中引用的解锁操作每个对象都有一个锁。 Java编程 语言没有提供的方法 执行单独的锁定和解锁 操作;相反,他们是 隐含地由高级别执行 总是安排配对的结构 这样的操作正确。 (Java 但是,虚拟机提供 单独的monitorenter和monitorexit 实现锁定的指令 并解锁操作。)
synchronized语句计算a 引用一个对象;然后呢 尝试执行锁定操作 在那个对象上,并没有继续 进一步直到锁定操作 成功完成。 (一把锁 操作可能会延迟,因为 关于锁的规则可以防止主要的 记忆从参与到一些 其他线程已准备好执行一个 或更多解锁操作。)之后 锁定操作已执行, 同步语句的主体是 执行。通常,编译器为 Java编程语言确保了这一点 由a实现的锁定操作 monitorenter指令执行 在执行机构之前 synchronized语句匹配 通过执行的解锁操作 每当一个monitorexit指令 synchronized语句完成, 完成是否正常或 突然的。
自动同步方法 它执行锁定操作 调用;它的身体没有被执行 直到锁定操作 成功完成。如果方法 是一个实例方法,它锁定了 与实例关联的锁定 它被调用(即, 将被称为此的对象 在执行方法期间 身体)。如果方法是静态的,那就是 锁定与之关联的锁 表示类的类对象 其中定义了方法。如果 执行方法的身体永远都是 完成,通常或 突然,解锁操作是 自动执行相同的操作 锁。
最佳做法是,如果变量是 永远被一个线程分配 由另一个使用或分配,然后全部 应该访问该变量 用同步方法或 同步声明。
虽然是Java的编译器 通常编程语言 保证结构化使用锁 (参见第7.14节“同步”), 无法保证所有代码 提交给Java虚拟机 将遵守这一财产。 Java虚拟的实现 机器是允许的但不是必需的 执行以下两个 保证结构化锁定的规则。
设T为线程,L为锁。 然后:
方法期间由T在L上执行的锁定操作的数量 调用必须等于数量 解锁由T执行的操作 在方法调用期间是否 方法调用完成 正常或突然。
- 醇>
在方法调用期间,任何时候都可以解锁 由T执行的操作 方法调用超过了 执行的锁定操作次数 自方法调用以来T on L.
在不太正式的术语中,在方法中 调用L上的每个解锁操作 必须匹配一些前面的锁 L的操作。
注意锁定和解锁 由Java自动执行 调用时的虚拟机 同步方法被认为是 在调用方法期间发生 调用
答案 4 :(得分:0)
同步是在多线程环境中编程时最重要的概念之一。 在使用同步时,必须考虑发生同步的对象。 例如,如果要同步静态方法,则同步必须使用
在类级别上synchronized(MyClass.class){
//code to be executed in the static context
}
如果要同步实例方法中的块,则同步必须使用在所有线程之间共享的对象的实例。 大多数人在第二点出错,因为它出现在你的代码中,同步似乎在不同的对象而不是单个对象上。