假设我有一个服务器,其中有多个线程共享对Data实例的引用。快点,
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); }
}
这非常粗糙,
我大多只关注并发性。
使用synchronized方法我是否正确假设同步会在类“Data”实例/对象上发生?因此,一个UploadThread可以调用put过程/方法,一个ReadThread可以并行执行相同的操作。但是,使用ReentrantLock示例,任何时候只有一个线程能够执行put调用?
如果在“Data”类中我将LinkedList设为静态并且我使put方法同步并且静态会发生什么?哪种方法最好?如果我把事情弄得一团糟,我会失去前卫吗?
答案 0 :(得分:4)
在Java中synchronized
部分是可重入的。这意味着单个线程可以根据需要多次进入同步部分,但只有当没有其他线程存在时,新线程才能进入。当前在这些部分中的线程已获得锁定,并且仅在离开所有同步部分时才返回锁定。除了通过方法签名声明synchronized
之外,还可以直接在对象上调用synchronized
。例如;这两种方法会产生同样的效果:
synchronized public void foo() {
}
public void foo() {
synchronized(this) {
}
}
ReentrantLock
与synchronized
非常相似,因为只有一个线程可以一次获取锁。如果一个线程达到lock.lock()
语句,它将等待锁被另一个线程解锁。如果线程已经锁定,它将继续。在单个同步代码块不够的更复杂情况下,这可能很有用。
如果......我使put方法同步并且静态会发生什么?
如果方法是static synchronized
,则表示您正在锁定类本身而不是类的实例。它独立于实例synchronized
方法锁定。
代码:
这里最简单的方法是将Data
对象转换为线程安全对象。如果您无法编辑此类的代码,那么一个有效的策略是将对象包装在线程安全包装器中。
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);