深入理解synchronized关键字

深入理解synchronized关键字

synchronized是java中的关键字 Java中的同步锁
使用synchronized使得代码变成同步代码
Java中的每一个对象都有一把琐 对象琐
类对象(Class对象)类锁

synchronized的三种用法

  • 用在代码块上
  • 用在方法声明上
  • 用在静态方法声明上

这三种用法作用的范围各有不同,锁对象也有差别


1.将synchronized关键字用在代码块上

举一个最普遍的例子,模拟银行转账
先建一个账户类
现在先不使用synchronized关键字查看转账情况

public class Account {
     
    private double balance = 10000;

    public double getBalance() {
     
        return balance;
    }

    public void setBalance(double balance) {
     
        this.balance = balance;
    }

    //取款方法
    public void withdrawal(double money){
     
            //获取账户中的余额
            double before = this.getBalance();
            System.out.println(Thread.currentThread().getName()+"取款前的余额:"+before);
            //取款之后的余额
            double after = before - money;
            //更新账户余额
            this.setBalance(after);
            System.out.println(Thread.currentThread().getName()+"取款后的余额:"+after);
    }
}

开启线程对象调用转账方法

public class AccountThread implements Runnable{
     
    Account account = new Account(); //两个线程共享的账户对象
    @Override
    public void run() {
     
        //调用取款方法
        account.withdrawal(5000);
    }

    public static void main(String[] args) {
     
        AccountThread accountThread = new AccountThread();
        Thread thread0 = new Thread(accountThread);
        thread0.start();

        Thread thread1 = new Thread(accountThread);
        thread1.start();
    }
}

测试结果:
深入理解synchronized关键字_第1张图片
很明显出现了错误,两个账户都进行了取款之后 账户余额还是5000,很明显在THread-0账户获取到余额的同时Thread-1也获取了余额 ,导致在Thread-0取完款之后Thread-1依旧使用的Thread-0取款之前的余额进行操作,导致两个账户都取完款之后账户余额依旧输出5000

使用synchronized关键字给withdrawal方法中的代码块进行加锁
目的就是当一个账户取款时另一个账户必须等待前面的账户操作完成才能进行操作
加锁代码:

        synchronized (this){
     
            //获取账户中的余额
            double before = this.getBalance();
            System.out.println(Thread.currentThread().getName()+"取款前的余额:"+before);
            //取款之后的余额
            double after = before - money;
            //更新账户余额
            this.setBalance(after);
            System.out.println(Thread.currentThread().getName()+"取款后的余额:"+after);
        }

再次测试查看结果:
深入理解synchronized关键字_第2张图片
使用synchronized之后取款正常
synchrionized(this)()中的this表示用的是当前Account对象的对象琐
因为两个线程对象共享一个Account 对象也就是this 想要对两个线程都起到上锁的作用必须要使用两个线程共有的对象的对象锁 即想要琐哪些线程对象就要找这些线程对象共有的对象锁就行了

2.用在方法声明上

将synchronized用在方法声明上表示琐的是整个方法,琐的范围比用在代码块上更大而且用在方面声明上默认琐的是this对象
加锁代码修改如下:

    //取款方法
    public synchronized void withdrawal(double money){
     
        //获取账户中的余额
        double before = this.getBalance();
        System.out.println(Thread.currentThread().getName()+"取款前的余额:"+before);
        //取款之后的余额
        double after = before - money;
        //更新账户余额
        this.setBalance(after);
        System.out.println(Thread.currentThread().getName()+"取款后的余额:"+after);
    }

运行结果:
取款正常
深入理解synchronized关键字_第3张图片

3.用在静态方法声明上

用在静态方法声明上琐的就是类对象 即Class对象 无论一个类的对象有多少个但是类对象只有一个
将withdrawal方法修改如下:

public class Account {
     
    private static double balance = 10000;

    //取款方法
    public static synchronized void withdrawal(double money){
     
        //获取账户中的余额
        double before = balance;
        System.out.println(Thread.currentThread().getName()+"取款前的余额:"+before);
        //取款之后的余额
        double after = before - money;
        //更新账户余额
        balance = after;
        System.out.println(Thread.currentThread().getName()+"取款后的余额:"+after);
    }
}

测试结果:
深入理解synchronized关键字_第4张图片

死锁:

死锁就是两个线程互相一直等待,一直等下去线程卡死
代码如下:

public class DeadLock {
     
    public static void main(String[] args) {
     
        Object o1 = new Object();
        Object o2 = new Object();

        Thread thread1 = new Thread(new MyThread1(o1, o2));
        thread1.start();
        Thread thread2 = new Thread(new MyThread2(o1, o2));
        thread2.start();
    }
}

class MyThread1 implements Runnable{
     
    private Object o1;
    private Object o2;
    public MyThread1(Object o1,Object o2){
     
        this.o1 = o1;
        this.o2 = o2;
    }
    @Override
    public void run() {
     
        synchronized (o1){
     
            try {
     
    //让线程休眠一秒 防止在线程2还没进入代码块时 线程1已经执行完了 对象琐都已经释放了
                Thread.sleep(1000); 
            } catch (InterruptedException e) {
     
                e.printStackTrace();
            }
            synchronized (o2){
     
                System.out.println("---11");
            }
        }

    }
}

class MyThread2 implements Runnable{
     
    private Object o1;
    private Object o2;

    public MyThread2(Object o1,Object o2){
     
        this.o1 = o1;
        this.o2 = o2;
    }
    @Override
    public void run() {
     
        synchronized (o2){
     
            try {
     
    //让线程休眠一秒 防止在线程1还没进入代码块时 线程2已经执行完了 对象琐都已经释放了
                Thread.sleep(1000);
            } catch (InterruptedException e) {
     
                e.printStackTrace();
            }
            synchronized (o1){
     
                System.out.println("---22");
            }
        }
    }
}

你可能感兴趣的:(java,多线程,编程语言,并发编程)