论坛首页 Java版

关于writer中锁的问题

浏览 3491 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
时间:2005-05-26
write中实现同步的是通过一个任意的Object实现的,我对此很迷惑,甚至很怀疑,对同步有点认识,所以才想向大家请教一下:

类库中Witer52行定义[code:1]protected Object lock;[/code:1] 说是用它来实现同步,要比锁住自身要好(指方法上直接synchronized) ,然后下面是它的应用:

类库中Witer86-94
[code:1]
public void write(int c) throws IOException {
synchronized (lock) {
if (writeBuffer == null){
writeBuffer = new char[writeBufferSize];
}
writeBuffer[0] = (char) c;
write(writeBuffer, 0, 1);
}
}
[/code:1]

我对上面的使用非常怀疑,我认为两个线程需要同步是因为有共同使用的数据,我通过将这个共用的数据(对象)加锁可以实现同步功能,所以这个被加锁的对象中一定有多个线程共同访问的数据,而这里的lock对象没有任何多线程向交互的数据,所以我认为这个同步操作是没有任何意义的,我想这样写会让人有一种错觉,就是不管你锁住的是什么对象,在synchronized区域中的数据都会是同步操作的,我看过很多书上都说过这是一个严重的错误观念,我也一直这样认为,而且也的确是这样的,最起码synchronized中的代码不是原子操作,而同样执行顺序是不定的,所以上面的东西让我很迷惑,这个是类库里的东西,没有道理会有问题,所以我认为应该是我认识上的错误,很希望大家能给我指正出我认识的误点,谢谢!!!!!!!

多线程本来并没有那么复杂,而总是被sun说成sophisticated,它同样没有优秀的文档阐述多线程的能力,有也是一些浮浅的东西,真不知道sun都做了什么.

我承认对多线程的东西不是很清楚,希望大家能给我一些帮助,谢谢
   
时间:2005-05-26
关于什么是 锁,lock, monitor. 这个帖子后面的整个讨论有详细的解释。
http://forum.javaeye.com/viewtopic.php?t=11315


buaawhl 写道
mochow 写道
synchronized(this),就是说锁住了this这个对象,mapToRead不是这个对象的一个变量吗?如果这个变量不被锁?那么被锁住的是什么?


synchronized(this) 的这个 this 表示这个 ReadWriteMap 的当前instance的object reference (4 bytes)。
this = 4 bytes
synchronized(this) 表示要获取 这个ReadWriteMap reference (4 bytes)上面的锁 lock (类似于 exclusive 信号量等),jvm spec称为monitor。
jvm中,一个 object reference (4 bytes) 唯一对应一个monitor (a structure).

buaawhl's old article 写道

2.2 Monitor = 锁
本文中会经常出现“monitor”和“锁”这两个词。本文中,这两个词的含义完全一样,可以互换。
monitor(锁)是Java虚拟机内部维护的一个结构,包含一些计数字段和一些队列(与操作系统的线程实现原理类似)。计数字段纪录队列里面线程的个数。
每个Java对象(Object)的reference(可以理解为对象的32-bit内存地址),都对应一个monitor结构。所以,任何reference类型的变量都可以提供monitor(锁),具有同步对象的资格。


不能说,锁住了这个对象。这个说法不对,不能反映实际的运行机制。
只能说, 获取了 这个同步对象 对应的锁。所以,我前面提到,同步对象最好声明为 final,这样它的 reference 才不会变化,才能保证 mutiple thread获得的是同一个 reference 对应的 锁 lock (or monitor).

只有Object Reference 类型 (4 bytes) 才有作为同步对象的资格。
primitive type, int, float, 都没有资格。
synchronized(10), 或 int a = 0; synchronzied(a) 都不行。


http://java.sun.com/docs/books/vmspec/2nd-edition/html/Threads.doc.html
引用

8.13 Locks and Synchronization

There is a lock associated with every object. The Java programming language does not provide a way to perform separate lock and unlock operations; instead, they are implicitly performed by high-level constructs that always arrange to pair such operations correctly. (The Java virtual machine, however, provides separate monitorenter and monitorexit instructions that implement the lock and unlock operations.)

The synchronized statement computes a reference to an object; it then attempts to perform a lock operation on that object and does not proceed further until the lock operation has successfully completed. (A lock operation may be delayed because the rules about locks can prevent the main memory from participating until some other thread is ready to perform one or more unlock operations.) After the lock operation has been performed, the body of the synchronized statement is executed. Normally, a compiler for the Java programming language ensures that the lock operation implemented by a monitorenter instruction executed prior to the execution of the body of the synchronized statement is matched by an unlock operation implemented by a monitorexit instruction whenever the synchronized statement completes, whether completion is normal or abrupt.

A synchronized method automatically performs a lock operation when it is invoked; its body is not executed until the lock operation has successfully completed. If the method is an instance method, it locks the lock associated with the instance for which it was invoked (that is, the object that will be known as this during execution of the method's body). If the method is static, it locks the lock associated with the Class object that represents the class in which the method is defined. If execution of the method's body is ever completed, either normally or abruptly, an unlock operation is automatically performed on that same lock.


有兴趣也可以查看, monitorenter, monitorexit 虚拟机指令。
http://java.sun.com/docs/books/vmspec/2nd-edition/html/Compiling.doc.html#6530
引用

The monitorenter and monitorexit instructions exist to support synchronized statements. For example:


void onlyMe(Foo f) {
synchronized(f) {
doSomething();
}
}

is compiled to

Method void onlyMe(Foo)
0 aload_1 // Push f
1 astore_2 // Store it in local variable 2
2 aload_2 // Push local variable 2 (f)
3 monitorenter // Enter the monitor associated with f
4 aload_0 // Holding the monitor, pass this and...
5 invokevirtual #5 // ...call Example.doSomething()V
8 aload_2 // Push local variable 2 (f)
9 monitorexit // Exit the monitor associated with f
10 return // Return normally
11 aload_2 // In case of any throw, end up here
12 monitorexit // Be sure to exit monitor...
13 athrow // ...then rethrow the value to the invoker
Exception table:
From To Target Type
4 8 11 any




楼主给出的这段代码。意味着 同一个instance的 write() 方法中,被synchronized 那一段 是被同步的。

其实,synchronized 如果加在instance 方法上,
相当于 synchronized(this), 和 synchronized(lock) 没有不同。只是获取的锁不同。
根本就不存在 “锁住自身” 这种事情。
   
0 请登录后投票
时间:2005-05-26
楼上写了这么多,我还是没有看清到底跟我问的问题有什么关系,我想知道线程获得了这个lock的锁,对同步起到什么作用,我认为它没有起到作用
   
0 请登录后投票
时间:2005-05-26
如果从jvm考虑,synchronized这个lock,应该先弹出这个lock对象,而synchronized的那块代码会在monitor的区域内,关键是同步区域中的内容没有同步作用,而只对加锁的对象敏感,所以它只表示了对加锁对象的敏感范围,并不是这范围内的指令要同步
   
0 请登录后投票
时间:2005-05-26
我一直在想一个问题,上面用lock对象这种方式本身就是一种标志,没有对lock中的数据进行锁定,如果是这样,那么这段代码的意义一定是说synchronized会对同步块区域的代码有作用,而我恰恰认为synchronized对同步块是不起作用的.

一个证明的例子:
[code:1]
package test;

class aa extends Thread{
String name;

public aa(String name) {
this.name = name;
}

public void run() {
med0();
}

public synchronized void med0() {
for (int i = 0; i <10; i++) {
int j = 0;
while (j < 1000) {
String lq = "lq" + System.currentTimeMillis();
j++;
}
System.out.print(name + i + " ");
}
}
}

public class Test {
public static void main(String[] args) throws Exception{
Thread t1 = new aa("aa");
Thread t2 = new aa("bb");

t1.start();
t2.start();
}
}
[/code:1]
   
0 请登录后投票
时间:2005-05-26
liqj2ee 写道
我一直在想一个问题,上面用lock对象这种方式本身就是一种标志,没有对lock中的数据进行锁定,如果是这样,那么这段代码的意义一定是说synchronized会对同步块区域的代码有作用,而我恰恰认为synchronized对同步块是不起作用的.

一个证明的例子:
[code:1]
package test;

class aa extends Thread{
String name;

public aa(String name) {
this.name = name;
}

public void run() {
med0();
}

public synchronized void med0() {
for (int i = 0; i <10; i++) {
int j = 0;
while (j < 1000) {
String lq = "lq" + System.currentTimeMillis();
j++;
}
System.out.print(name + i + " ");
}
}
}

public class Test {
public static void main(String[] args) throws Exception{
Thread t1 = new aa("aa");
Thread t2 = new aa("bb");

t1.start();
t2.start();
}
}
[/code:1]


首先弄清楚同步的条件,它起码是指多个线程访问同一个资源的时候。而你的代码中根本还没有达到这一最起码的条件。你起了两个线程,但两个线程并没有争夺同一个资源。
   
0 请登录后投票
时间:2005-05-26
引用
我看过很多书上都说过这是一个严重的错误观念
请举证.

使用锁的充要条件是:
多个线程实例争用共享资源

例如:
假设有共享资源对象A
场景1:
线程1:
synchronized (A){do something...}
线程2:
Object other = A;
这里不存在争用问题.同时不小心的话可能会造成同步问题.
场景2:
线程1:
synchronized (A){do something1...}
线程2:
synchronized (A){do something2...}
这里存在争用.但是不会造成同步问题.

也就是说,不显式声明需要同步的话,系统是不会为开发者考虑同步的.
   
0 请登录后投票
时间:2005-05-26
楼主其实误解了synchronized method的意思.
例子里的同步锁是针对实例对象的实例方法的
你如果想针对某Class的而不是实例执行同步method方法,应该作如下声明:
static synchronized method
   
0 请登录后投票
时间:2005-05-26
[code:1]public void write(int c) throws IOException {
synchronized (lock) {
if (writeBuffer == null){
writeBuffer = new char[writeBufferSize];
}
writeBuffer[0] = (char) c;
write(writeBuffer, 0, 1);
}
} [/code:1]这代码是没问题的,一般我们会写成synchronized(this)或者把方法声明为synchronized,但在有些情况下,你不想让外部看到这个锁(也就是this)的时候,就可以象上面这么写。如果外部可以得到这个锁,那别人也可以synchronized那个对象(不管是无意的还是恶意的),从而造成一些问题。
你可以找些资料看看就明白了
   
0 请登录后投票
时间:2005-05-27
wolfsquare 写道
楼主其实误解了synchronized method的意思.
例子里的同步锁是针对实例对象的实例方法的
你如果想针对某Class的而不是实例执行同步method方法,应该作如下声明:
static synchronized method


没错。通过上面的一系列的帖子和代码,我想,楼主要的就是这个,
synchronized static method

synchronized static method 相当于 "synchronized (thisClass)".
synchronized instance method 相当于 "synchronized(this)".

因为楼主的帖子里面,writer的write() method就是 instance method.
我的帖子里面,特意强调了 instance method.

所以,不要怪我 答所非问。我一开始总是假设 一些基本功常识 是共同探讨的基础,不可能预先知道,并负责弥补所有的知识点。

(借题发挥一下。下面的话还是针对现象,而不针对个人。类似的语言,我反复呼吁过多次了,这次再罗嗦一次。大家见谅。:-)
其实,我的帖子里面给出Sun的两个关于Thread的link,里面详细阐述了Thread的基本内容。上面贴子讨论的所有内容,都包括在里面。只要抱着耐心的探个究竟的精神,仔细阅读理解,一切都迎刃而解了。可是,看来,很多人不太愿意花功夫在基础上,而只愿意获得一些比较cool的结论。长久来看,这是舍本逐末的)。
   
0 请登录后投票
论坛首页 Java版

跳转论坛:
JavaEye推荐