Java 中的对象锁是如何管理的?

线程
对象锁
synchronized
多线程
java

#1

每个锁对象都有两个队列,一个是就绪队列,一个是阻塞队列,就绪队列存储了将要获得锁的线程,阻塞队列存储了被阻塞的线程,当一个被线程被唤醒 (notify)后,才会进入到就绪队列,等待获得锁。平时说的等待队列也就是阻塞队列。

例如:当一开始线程a第一次执行对象 account. deposit() 方法时,JVM会检查锁对象 account 的就绪队列是否已经有线程在等待,如果有则表明 account 的锁已经被占用了,由于是第一次运行,account 的就绪队列为空,所以线程 a 获得了锁, 执行 account. deposit 方法。如果恰好在这个时候,线程 b 要执行 account.withdraw 方法,因为线程 a 已经获得了锁还没有释放,所以线程 b 要进入 account 的就绪队列,等到得到锁后才可以执行。一个线程执行临界区代码过程如下:

1.获得同步锁
2.清空工作内存
3.从主存拷贝变量副本到工作内存
4.对这些变量计算
5.将变量从工作内存写回到主存
6.释放锁

可见,synchronized 既保证了多线程的并发有序性,又保证了多线程的内存可见性。notify 不会释放锁,而是通知锁对象的阻塞队列里的某一线程(被阻塞,即主动调用 wait 方法),进入就绪队列。线程释放锁的方式,通常是 主动调用 wait 方法、同步代码块结束释放锁资源。notifyall 是 唤醒阻塞队列里的所有阻塞线程,他们都将进入就绪队列,而 notify 的数量是一个。同步代码块结束释放锁资源,对象就绪队列中的某一线程获得锁资源而开始线程;如果不使用 notify 那么阻塞队列 里 线程将一直处于阻塞状态,即使就绪队列里 线程都执行完了,阻塞队列 里 线程也将一直处于阻塞状态.某一线程释放锁之后,将会从就绪队列中 按一定策略(随机?)找出一线程,使之获得锁资源。


#2

测试代码:
Account class

    public class Account {
    public static Account account;
    private static int balance = 1000;
    private static Person person;

    private Account() {
    }

    public static Account getAccount(Person p) {
        if (account == null) {
            account = new Account();
        }
        Account.person = p;
        return account;
    }

    public static int getBal() {
        return balance;
    }

    public synchronized void withdraw(int bal) {
        try {

            if (balance >= bal) {
                System.out.println(person.getName() + " " + "is try to withdraw");
                try {
                    Thread.sleep(100);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                balance = balance - bal;
                System.out.println(person.getName() + " " + "is complete the withdraw");
            } else {
                System.out.println(person.getName() + " " + "doesn't have enough money for withdraw ");
            }
            System.out.println(person.getName() + " " + " withdraw Rs." + balance);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public synchronized void deposit(int bal) {
        try {
            if (bal > 0) {
                System.out.println(person.getName() + " " + " is try to deposit");
                try {
                    Thread.sleep(100);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                balance = balance + bal;
                System.out.println(person.getName() + " " + "is complete the deposit");
            } else {
                System.out.println(person.getName() + " " + "doesn't have enough money for deposit");
            }
            System.out.println(person.getName() + " " + " deposit Rs." + balance);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }}

Person class

    public class Person {
    private String name;

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return name;
    }}

ThreadExercise class

    public class ThreadExercise extends Thread implements Runnable {

    private Person person;

    public ThreadExercise(Person p) {
        this.person = p;
    }

    public static void main(String[] args) {

        ThreadExercise ts1 = new ThreadExercise(new Person("person 1"));
        ts1.start();
        ThreadExercise ts2 = new ThreadExercise(new Person("person 2"));
        ts2.start();
        ThreadExercise ts3 = new ThreadExercise(new Person("person 3"));
        ts3.start();

    }

    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            try {
                Account acc = Account.getAccount(person);
                acc.withdraw(100);
                try {
                    Thread.sleep(200);
                } catch (InterruptedException ex) {
                    Logger.getLogger(ThreadExercise.class.getName()).log(Level.SEVERE, null, ex);
                }
                if (acc.getBal() < 0) {
                    System.out.println("account is overdrawn!");
                }
                acc.deposit(200);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println("Final Acc balance is Rs." + Account.getBal());
    }}