线程的锁synchronized

一:锁的概念

只有获取到这个锁后才会执行相应的代码,可以用任意对象当作锁
例如:

public class T {
    private int count = 0;

    Object o = new Object();
    public void m() {
        synchronized(o) {
            count ++;
            System.out.println(Thread.currentThread().getName()+"count="+count);
        }
    }
}

二:锁的几种方式

  1. synchronized(this) // 锁定当前对象,获取到this锁才会执行相应代码
  2. public synchronized void k() {} // 等同于synchronized(this)
  3. 静态方法加锁
public class T {
    private static int count = 0;
    // 等同于synchronized(T.class)
    public synchronized static void n () {
        count ++;
        System.out.println(Thread.currentThread().getName()+"count="+count);
    }
}

三:锁的使用场景

比如多个线程对同一个数据进行读写,若不加锁将可能会出现脏读
例子:

/**
 * 模拟银行账户
 * 对业务写方法加锁
 * 对业务读方法不加锁
 *
 * 这样将会容易出现脏读:即读到的内容是写过程前的数据,此时正在写但还未写完成
 * @author 86152
 */
public class Account {
    String name;
    Double balance = 0.0;

    public synchronized void set(String name,Double balance) {
        this.name = name;
        try {
            Thread.sleep(2000);
        }catch (Exception e){
            e.printStackTrace();
        }
        this.balance = balance;
    }

    public Double get() {
        return balance;
    }

    public static void main(String[] args) {
        Account account = new Account();
        new Thread(()->{
            account.set("zhangsan",100.0);
        }).start();

        try {
            TimeUnit.SECONDS.sleep(1);
        }catch (Exception e){
            e.printStackTrace();
        }

        System.out.println(account.get());

        try {
            TimeUnit.SECONDS.sleep(2);
        }catch (Exception e){
            e.printStackTrace();
        }

        System.out.println(account.get());

    }

}

结果:

0.0
100.0

读写均加锁后:

public class Account {
    String name;
    Double balance = 0.0;

    public synchronized void set(String name,Double balance) {
        this.name = name;
        try {
            Thread.sleep(2000);
        }catch (Exception e){
            e.printStackTrace();
        }
        this.balance = balance;
    }

    public synchronized Double get() {
        return balance;
    }

    public static void main(String[] args) {
        Account account = new Account();
        new Thread(()->{
            account.set("zhangsan",100.0);
        }).start();

        try {
            TimeUnit.SECONDS.sleep(1);
        }catch (Exception e){
            e.printStackTrace();
        }

        System.out.println(account.get());

        try {
            TimeUnit.SECONDS.sleep(2);
        }catch (Exception e){
            e.printStackTrace();
        }

        System.out.println(account.get());

    }

}

加锁后结果:

100.0
100.0

A获取到锁后,在A未执行完并释放锁时B不能执行同样需要锁的代码。只有等A释放锁后B获取到锁B才能继续执行。

四:synchronized是可重入锁

public synchronized void m() {
m1();
}

public synchronized void m1() {
}

一个同步方法可以调用另外一个同步方法,一个线程已经拥有某个对象的锁,再次申请的时候仍然会得到该对象的锁。也就是说synchronized获得的锁是可重入的

五:异常和锁

程序在执行过程中,如果出现异常,默认情况下锁会被释放。所以在并发处理过程中,要注意同步代码中出现异常可能会出现数据不一致情况。
例如:多线程访问同一资源,如果异常处理不合适,在第一个线程中抛出异常,其它线程将会进入同步代码区,有可能会访问到异常产生时的数据。
因此:处理同步业务逻辑时要谨慎处理异常

六:总结

1.在非必要加锁的情况下,能不加锁就不加,因为加锁后会严重影响效率
2.同步代码块中的代码越少越好,能提高效率。但若有较多同步代码块,也需适当合并成较少代码块
3.用作锁对象的对象最好加上finalfinal Object o = new Object();
4.基本类型不要用来做锁对象

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值