同步方法与 ReentrantLock
Synchronized Method vs ReentrantLock
假设我有一个具有多个线程的服务器,这些线程共享对数据实例的引用。快速例如,
edit1:更新以提高可读性
public void main() {
Data data = new Data();
ReentrantLock rl = new ReentrantLock(true);
ReadThread t1 = new ReadThread(data, rl);
UploadThread t2 = new UploadThread(data, rl);
t1.start(); t2.start();
}
class ReadThread extends Thread {
private Data data;
private ReentrantLock lock;
ReadThread(Data d, ReentrantLock l){ data = d; lock = l; }
void run(){
lock.lock();
try{
data.put(aString)
} finally { lock.unlock(); }
}
}
class UploadThread extends Thread {
private Data data;
private ReentrantLock lock;
UploadThread(Data d, ReentrantLock l){ data = d; lock = l; }
void run(){
lock.lock();
try{
data.put(aString)
} finally { lock.unlock(); }
}
}
是像上面那样使用锁,还是像下面这样同步put方法比较好,
class Data {
private LinkedList<String> data = new LinkedList<String>();
synchronized void put(String x){ data.add(x); }
}
这很粗糙,
我主要只关心并发性。
使用同步方法,我假设同步将发生在 class' "Data" instance/object 上是否正确?因此,一个 UploadThread 可以调用 put procedure/method 并且一个 ReadThread 可以并行执行相同的操作。但是使用 ReentrantLock 示例,在任何时候只有一个线程能够执行 put 调用?
如果在 "Data" class 中我将 LinkedList 设置为静态,并将 put 方法设置为同步和静态,会发生什么情况?哪种方法最好?如果我让事情变得静态,我会失去 mut ex 吗?
在 Java synchronized
部分是可重入的。这意味着单个线程可以根据需要多次进入同步部分,但是新线程只有在没有其他线程存在时才能进入。当前位于这些部分中的线程已获得锁,并且只会 return 在离开所有同步部分时获得锁。除了通过方法签名声明 synchronized
之外,synchronized
也可以直接在对象上调用。例如;这两种方法的效果是一样的:
synchronized public void foo() {
}
public void foo() {
synchronized(this) {
}
}
ReentrantLock
与synchronized
非常相似,一次只能有一个线程获取锁。如果线程到达 lock.lock()
语句,它将等待直到锁被另一个线程解锁。如果线程已经有锁,它将继续。这在单个同步代码块不够用的更复杂情况下很有用。
What would happen if ... I made the put method synchronized and static?
如果方法是 static synchronized
,这意味着您正在锁定 class 本身,而不是 class 的实例。它独立于实例 synchronized
方法被锁定。
对于您的代码:
这里最简单的做法是将 Data
对象变成线程安全对象。如果您无法编辑此 class 的代码,那么一种有效的策略是将该对象包装在线程安全的包装器中。
interface Foo {
void bar();
}
class UnsafeFoo implements Foo {
@Override bar() { ... }
}
class ThreadSafeFoo implements Foo {
Foo foo;
ThreadSafeFoo(Foo foo) { this.foo = foo; }
@Override synchronized bar() { foo.bar(); }
}
Foo unsafe = new UnsafeFoo();
Foo safe = new ThreadSafeFoo(unsafe);
假设我有一个具有多个线程的服务器,这些线程共享对数据实例的引用。快速例如,
edit1:更新以提高可读性
public void main() {
Data data = new Data();
ReentrantLock rl = new ReentrantLock(true);
ReadThread t1 = new ReadThread(data, rl);
UploadThread t2 = new UploadThread(data, rl);
t1.start(); t2.start();
}
class ReadThread extends Thread {
private Data data;
private ReentrantLock lock;
ReadThread(Data d, ReentrantLock l){ data = d; lock = l; }
void run(){
lock.lock();
try{
data.put(aString)
} finally { lock.unlock(); }
}
}
class UploadThread extends Thread {
private Data data;
private ReentrantLock lock;
UploadThread(Data d, ReentrantLock l){ data = d; lock = l; }
void run(){
lock.lock();
try{
data.put(aString)
} finally { lock.unlock(); }
}
}
是像上面那样使用锁,还是像下面这样同步put方法比较好,
class Data {
private LinkedList<String> data = new LinkedList<String>();
synchronized void put(String x){ data.add(x); }
}
这很粗糙,
我主要只关心并发性。
使用同步方法,我假设同步将发生在 class' "Data" instance/object 上是否正确?因此,一个 UploadThread 可以调用 put procedure/method 并且一个 ReadThread 可以并行执行相同的操作。但是使用 ReentrantLock 示例,在任何时候只有一个线程能够执行 put 调用?
如果在 "Data" class 中我将 LinkedList 设置为静态,并将 put 方法设置为同步和静态,会发生什么情况?哪种方法最好?如果我让事情变得静态,我会失去 mut ex 吗?
在 Java synchronized
部分是可重入的。这意味着单个线程可以根据需要多次进入同步部分,但是新线程只有在没有其他线程存在时才能进入。当前位于这些部分中的线程已获得锁,并且只会 return 在离开所有同步部分时获得锁。除了通过方法签名声明 synchronized
之外,synchronized
也可以直接在对象上调用。例如;这两种方法的效果是一样的:
synchronized public void foo() {
}
public void foo() {
synchronized(this) {
}
}
ReentrantLock
与synchronized
非常相似,一次只能有一个线程获取锁。如果线程到达 lock.lock()
语句,它将等待直到锁被另一个线程解锁。如果线程已经有锁,它将继续。这在单个同步代码块不够用的更复杂情况下很有用。
What would happen if ... I made the put method synchronized and static?
如果方法是 static synchronized
,这意味着您正在锁定 class 本身,而不是 class 的实例。它独立于实例 synchronized
方法被锁定。
对于您的代码:
这里最简单的做法是将 Data
对象变成线程安全对象。如果您无法编辑此 class 的代码,那么一种有效的策略是将该对象包装在线程安全的包装器中。
interface Foo {
void bar();
}
class UnsafeFoo implements Foo {
@Override bar() { ... }
}
class ThreadSafeFoo implements Foo {
Foo foo;
ThreadSafeFoo(Foo foo) { this.foo = foo; }
@Override synchronized bar() { foo.bar(); }
}
Foo unsafe = new UnsafeFoo();
Foo safe = new ThreadSafeFoo(unsafe);