首页 > 代码库 > java线程安全

java线程安全

可见性

?
1
Java内存模型(JMM)规定了jvm有主内存,主内存是多个线程共享的。当new一个对象的时候,也是被分配在主内存中,<br> 每个线程都有自己的工作内存,工作内存存储了主存的某些对象的副本,当然线程的工作内存大小是有限制的。

  (1) 从主存复制变量到当前工作内存 (read and load)
 (2) 执行代码,改变共享变量值 (use and assign)
 (3) 用工作内存数据刷新主存相关内容  (store and write)

有序性

线程在引用变量时不能直接从主内存中引用,如果线程工作内存中没有该变量,则会从主内存中拷贝一个副本到工作内存中,这个过程为read-load,完成后线程会引用该副本。当同一线程再度引用该字段时,有可能重新从主存中获取变量副本(read-load-use),也有可能直接引用原来的副本(use),也就是说 read,load,use顺序可以由JVM实现系统决定。
        线程不能直接为主存中中字段赋值,它会将值指定给工作内存中的变量副本(assign),完成后这个变量副本会同步到主存储区(store-write),至于何时同步过去,根据JVM实现系统决定.有该字段,则会从主内存中将该字段赋值到工作内存中,这个过程为read-load,完成后线程会引用该变量副本,当同一线程多次重复对字段赋值时

public class Account {
    private int balance;
    public Account(int balance) {
        this.balance = balance;
    }
    public int getBalance() {
        return balance;
    }
    public synchronized void add(int num) {
        balance = balance + num;
        System.out.println(Thread.currentThread().getName() + "-" + balance);
    }
    public synchronized void withdraw(int num) {
        balance = balance - num;
        System.out.println(Thread.currentThread().getName() + "-" + balance);
    }
    public static void main(String[] args) throws InterruptedException {
        Account account = new Account(1000);
        Thread a = new Thread(new AddThread(account, 20), "add");
        Thread b = new Thread(new WithdrawThread(account, 20), "withdraw");
        a.start();
        b.start();
        a.join();
        b.join();
        System.out.println(account.getBalance());
    }
    static class AddThread implements Runnable {
        Account account;
        int     amount;

        public void run() {
            for (int i = 0; i < 200000; i++) {
                account.add(amount);
            }
        }
        
        public AddThread(Account account, int amount) {
            this.account = account;
            this.amount = amount;
        }
    }

    static class WithdrawThread implements Runnable {
        Account account;
        int     amount;

        public WithdrawThread(Account account, int amount) {
            this.account = account;
            this.amount = amount;
        }

        public void run() {
            for (int i = 0; i < 100000; i++) {
                account.withdraw(amount);
            }
        }
    }
}

只有在对象锁池中的线程才有机会获得对象锁.执行同步代码块.

如果需要获得对象锁.线程需要从等待池.-移动->锁池-进一步->获得对象锁.

 

释放对象的锁<以下情况会释放对象锁>

1 执行完同步代码块就会释放锁

2 执行同步代码块过程中,遇到异常而导致线程终止锁被释放

3 在执行同步代码块过程中,遇到wait().执行该方法的线程

释放对象的锁.把该县城放到该对象的等待池中.