Java 收集对象本身以进行迭代;因为迭代由多个方法调用组成,所以不能在内部完全保护它

Java 收集对象本身以进行迭代;因为迭代由多个方法调用组成,所以不能在内部完全保护它,java,multithreading,synchronized,Java,Multithreading,Synchronized,在后一种情况下,我使用普通对象: private Object mutex=new Object(); 然而,在看到许多JVM转储和堆栈跟踪表明锁是“java.lang.Object()的实例”之后,我不得不说,正如其他人所建议的那样,使用内部类可能更有用 不管怎样,这是我的两分钱 编辑:还有一件事,当在这个上同步时,我更喜欢同步方法,并保持方法非常精细。我认为它更清晰、更简洁。关于同步什么取决于可能与此方法调用冲突的其他线程可以同步什么 如果此是一个仅由一个线程使用的对象,并且我们正在访问线

在后一种情况下,我使用普通对象:

private Object mutex=new Object();
然而,在看到许多JVM转储和堆栈跟踪表明锁是“java.lang.Object()的实例”之后,我不得不说,正如其他人所建议的那样,使用内部类可能更有用

不管怎样,这是我的两分钱


编辑:还有一件事,当在
这个
上同步时,我更喜欢同步方法,并保持方法非常精细。我认为它更清晰、更简洁。

关于同步什么取决于可能与此方法调用冲突的其他线程可以同步什么

如果
是一个仅由一个线程使用的对象,并且我们正在访问线程之间共享的可变对象,那么一个很好的候选者就是在该对象上同步-在
上同步此
没有意义,因为修改该共享对象的另一个线程可能甚至不知道
,但他知道那个物体

另一方面,如果多个线程同时调用此对象的方法(例如,如果我们处于单实例中),则通过
进行同步是有意义的


请注意,同步方法通常不是最佳选择,因为我们在方法运行的整个过程中都持有一个锁。如果它包含耗时但线程安全的部分,而不是耗时的线程不安全部分,那么通过该方法进行同步是非常错误的

几乎所有的块都在这上面同步,但这有什么特别的原因吗?还有其他的可能性吗

此声明同步整个方法

private synchronized void doSomething() {
private void doSomething() {
  // thread-safe code
  synchronized(this) {
    // thread-unsafe code
  }
  // thread-safe code
}
此声明同步了部分代码块而不是整个方法

private synchronized void doSomething() {
private void doSomething() {
  // thread-safe code
  synchronized(this) {
    // thread-unsafe code
  }
  // thread-safe code
}
来自oracle文档

使这些方法同步有两个效果:

首先,同一对象上的两个同步方法调用不可能交错。当一个线程为一个对象执行同步方法时,调用同一对象块的同步方法的所有其他线程(暂停执行),直到第一个线程对该对象执行完毕

还有其他的可能性吗?对于要在哪个对象上进行同步,是否有最佳做法?(例如对象的私有实例?)

同步有许多可能性和替代方案。您可以通过使用高级并发(自JDK1.5发行版起提供)使代码线程安全

有关更多详细信息,请参阅以下SE问题:


最佳做法是创建一个仅用于提供锁的对象:

private final Object lock = new Object();

private void doSomething() {
  // thread-safe code
  synchronized(lock) {
    // thread-unsafe code
  }
  // thread-safe code
}
通过这样做,您就安全了,没有任何调用代码可以通过无意中的
synchronized(yourObject)
行使您的方法死锁

(这要归功于@jared和@yuval adam,他们在上面更详细地解释了这一点。)


我的猜测是,在教程中使用
这个
的流行源于早期的Sun javadoc:

同步包括3个部分:原子性、可见性和顺序

同步块是非常粗糙的同步级别。它加强了可视性和有序性,正如您所期望的那样。但对于原子性来说,它并没有提供太多的保护。原子性需要程序的全局知识,而不是局部知识。(这使得多线程编程非常困难)

假设我们有一个类
帐户
有方法
存款
取款
。它们都是基于私有锁同步的,如下所示:

class Account {
    private Object lock = new Object();

    void withdraw(int amount) {
        synchronized(lock) {
            // ...
        }
    }

    void deposit(int amount) {
        synchronized(lock) {
            // ...
        }
    }
}
class AccountManager {
    void transfer(Account fromAcc, Account toAcc, int amount) {
        if (fromAcc.getBalance() > amount) {
            fromAcc.setBalance(fromAcc.getBalance() - amount);
            toAcc.setBalance(toAcc.getBalance + amount);
        }
    }
}
考虑到我们需要实现一个更高级别的类来处理传输,如下所示:

class Account {
    private Object lock = new Object();

    void withdraw(int amount) {
        synchronized(lock) {
            // ...
        }
    }

    void deposit(int amount) {
        synchronized(lock) {
            // ...
        }
    }
}
class AccountManager {
    void transfer(Account fromAcc, Account toAcc, int amount) {
        if (fromAcc.getBalance() > amount) {
            fromAcc.setBalance(fromAcc.getBalance() - amount);
            toAcc.setBalance(toAcc.getBalance + amount);
        }
    }
}
假设我们现在有两个账户

Account john;
Account marry;
如果
帐户存款()
帐户提款()
仅用内部锁锁定。当我们有两个线程工作时,这将导致问题:

// Some thread
void threadA() {
    john.withdraw(500);
}

// Another thread
void threadB() {
    accountManager.transfer(john, marry, 100);
}
因为
threadA
threadB
可以同时运行。线程B完成条件检查,线程A退出,线程B再次退出。这意味着我们可以从约翰那里提取100美元,即使他的账户没有足够的钱。这将打破原子性

您可能会建议:为什么不将
draw()
deposit()
添加到
AccountManager
中呢?但是在这个提议下,我们需要创建一个多线程安全的
Map
,它将不同帐户映射到它们的锁。我们需要在执行后删除锁(否则会泄漏内存)。我们还需要确保没有其他人直接访问
Account.draw()
。这将引入许多微妙的bug

正确且最惯用的方法是公开
帐户中的锁。然后让
AccountManager
使用锁。但是在这种情况下,为什么不直接使用对象本身呢

class Account {
    synchronized void withdraw(int amount) {
        // ...
    }

    synchronized void deposit(int amount) {
        // ...
    }
}

class AccountManager {
    void transfer(Account fromAcc, Account toAcc, int amount) {
        // Ensure locking order to prevent deadlock
        Account firstLock = fromAcc.hashCode() < toAcc.hashCode() ? fromAcc : toAcc;
        Account secondLock = fromAcc.hashCode() < toAcc.hashCode() ? toAcc : fromAcc;

        synchronized(firstLock) {
            synchronized(secondLock) {
                if (fromAcc.getBalance() > amount) {
                    fromAcc.setBalance(fromAcc.getBalance() - amount);
                    toAcc.setBalance(toAcc.getBalance + amount);
                }
            }
        }
    }
}
类帐户{
同步作废取款(整笔金额){
// ...
}
同步作废存款(整笔金额){
// ...
}
}
班级会计经理{
无效转账(账户从ACC、账户到ACC、int金额){
//确保锁定顺序以防止死锁
Account firstLock=fromac.hashCode()金额){
fromAcc.setBalance(fromAcc.getBalance()-金额);
toAcc.setBalance(toAcc.getBalance+金额);
}
}
}
}
}
用简单的英语总结一下,私有锁不适用于稍微复杂的多线程程序。

谁会试图在对象上“阻止您的同步”,从而使在此对象上的同步变得危险、不实用或不可用
// Some thread
void threadA() {
    john.withdraw(500);
}

// Another thread
void threadB() {
    accountManager.transfer(john, marry, 100);
}
class Account {
    synchronized void withdraw(int amount) {
        // ...
    }

    synchronized void deposit(int amount) {
        // ...
    }
}

class AccountManager {
    void transfer(Account fromAcc, Account toAcc, int amount) {
        // Ensure locking order to prevent deadlock
        Account firstLock = fromAcc.hashCode() < toAcc.hashCode() ? fromAcc : toAcc;
        Account secondLock = fromAcc.hashCode() < toAcc.hashCode() ? toAcc : fromAcc;

        synchronized(firstLock) {
            synchronized(secondLock) {
                if (fromAcc.getBalance() > amount) {
                    fromAcc.setBalance(fromAcc.getBalance() - amount);
                    toAcc.setBalance(toAcc.getBalance + amount);
                }
            }
        }
    }
}