Synchronized是否保留锁定

时间:2014-11-25 14:17:28

标签: java multithreading synchronization

考虑代码:

class Account {
  private int balance=50;
  public int getBalance(){
    return balance;
    }
    public void withdraw(int amt){
        balance=balance-amt;
        }
    }
public class AccountBalance implements Runnable {

    private Account acc=new Account();

    public static void main(String[] args) {
        AccountBalance accBal=new AccountBalance();
        Thread one=new Thread(accBal);
        Thread two=new Thread(accBal);
        one.setName("Thread One");
        two.setName("thread Two");
        one.start();
        two.start();
    }
    public void run(){
        for(int i=0;i<5;i++){
            makeWithdrawal(10);
        }
    }
    public synchronized void makeWithdrawal(int amount){
        if(acc.getBalance()>=amount){
            System.out.println(Thread.currentThread().getName()+" is going to withdraw "+acc.getBalance());
        try{
            Thread.sleep(500);
        }
        catch(InterruptedException ie){}
        acc.withdraw(amount);               //line 1
        System.out.println(Thread.currentThread().getName()+" completes the withdrawal");
        }
        else {
            System.out.println("Not enough in account for "+Thread.currentThread().getName()+" to withdraw "+acc.getBalance());
        }
    }
}

1)假设 Thread Fred 进入同步块并锁定对象 accBal ,然后在中间某处 Thread Fred 调用另一个方法(第1行)哪个不同步,那么线程不释放锁为什么?如果线程从synchronized发出,那么它应该释放锁定,或者锁定直到同步块。

2)假设我还有一个方法说 changeAccount ,这个方法不同步,然后一个新的线程说线程三,它具有相同的对象 accBal 将进入该方法并可以更改违反我们的同步代码的变量余额。

那么Java提供的是什么,即使存在非同步代码并且线程对某个对象具有锁定,那么另一个线程不应该输入具有相同对象的非同步代码一旦锁具有除非锁被释放,否则Thread不会对该对象采取行动。

4 个答案:

答案 0 :(得分:2)

  1. 虽然从另一个方法执行代码,但该线程仍在执行原始的synchronized方法。这个其他方法执行只是原始同步方法执行的一部分。

  2. 这是正确的,任何时候任何线程都可以更改不受保护的值。

  3. 还有其他方法可以执行互斥(例如使用CountDownLatch),但如果没有完全同步关键部分,则可能会遇到多线程内存问题,例如陈旧值。

答案 1 :(得分:1)

  1)假设线程Fred进入同步块并获取对象accBal的锁定然后在中间的某个地方Thread调用另一个未同步的方法(第1行),那么线程不会释放锁定为什么?

考虑以下代码

synchronized (accBal) {
    method()
}
// this line has to be reached before the lock on accBal is released

当调用method()时,它不会退出同步块,同步块被推送&#39;在堆栈上并在method()的调用返回(退出)时保留。因此,在调用method()期间不会释放锁。

  

2)假设我还有一个方法,即changeAccount不同步,然后一个新的线程说具有相同对象的线程三,accBal将进入该方法,并且可以更改违反我们的同步代码的变量余额。

正确。它会违反同步,并且预测行为会有点棘手,线程之间任何数据变化的可见性都会导致数据争用和引擎优化的副作用。因此,重要的是每个人都要尊重同步块。

  

3)所以Java提供了什么,即使存在非同步代码并且Thread正在对某个对象进行锁定,那么另一个Thread也不应该输入具有相同对象的非同步代码。锁已经通过线程然后没有线程将作用于该对象,除非锁被释放。

在Java中,所有悲观的锁定形式都要求每个线程彼此都能玩得很好并检查锁定。理由是执行并发检查会产生成本,而且大多数代码都不需要这些开销。

答案 2 :(得分:1)

  1. 同步块仅在退出时释放锁定。没有非常好的理由为什么就是这种情况,如果您的逻辑能够处理随着您在方法中前进而变化的成员变量,那么您就不会受到约束。使用ReadWrite锁(可能是ReentrantReadWriteLock)并研究它们提供的模式。
  2. 如果您有时使用锁定访问成员变量,有时没有锁定,那么您可能根本就没有它们。当然,像SONAR这样的产品会用一个大红色标志来标记这种模式。同样,您可能希望使用ReadWriteLock的细粒度方法。

答案 3 :(得分:0)

没有。如果您有一个访问应该受保护的数据的方法,那么该方法(或其中的关键代码)应该同步(在同一个对象上)。如果保持方法不同步,则意味着程序员认为它访问的数据不需要保护。

您可以使用其他锁定方法,但没有任何方法可以使Java锁定程序员认为不值得锁定的内容。锁定会带来性能损失,因此不受保护的代码(程序员认为安全)不应自动受到保护。