Java并发编程|第三篇:synchronized锁

系列文章

1.锁的应用场景

先来看一个例子,线程A、线程B或者多个线程同时操作count,进行++操作,结果有可能和预期不同
在这里插入图片描述

测试代码

在正常情况下,以下代码的结果应该为1000,循环1001次,每次+1;

public class AtomicDemo {

    private static int count = 0;

    public static void incr() {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            count++;
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i <= 1000; i++) {
            new Thread(() -> {
                AtomicDemo2.incr();
            }).start();
        }
        Thread.sleep(5000);
        System.out.println(count);
    }
}

实际运行结果(而且每次都可能不一样):
在这里插入图片描述

原因

线程A对count++之后,count的值需要同步到共享资源(主内存)中,然后线程B再去获取共享资源中的count,这样得到的值才是正确的

解决方法

在这里插入图片描述

在线程访问共享资源之前加一把锁synchronized

线程A先获得这把锁,操作count++,在count值同步到共享资源(主内存)过程中,线程B如果想要操作count,也需要获得这把锁才能操作,必须要等线程A释放锁,此时线程B处于阻塞状态,当线程A将值同步到共享资源(主内存)中后,释放锁,线程B拿到锁才能操作count。

2.锁的粒度

synchronized 有三种使用方法

  • 1.修饰实例方法
  • 2.修饰静态方法
  • 3.修饰代码块

2.1修饰实例方法

对类的实例方法加锁

    public synchronized void demo() {
       //TODO
    }

测试代码

public class AtomicDemo3 {

    private static int count = 0;

    public synchronized void incr() {
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        count++;
    }

    public static void main(String[] args) throws InterruptedException {
        AtomicDemo3 demo3 = new AtomicDemo3();
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> {
                demo3.incr();
            }).start();
        }
        Thread.sleep(5000);
        System.out.println(count);
    }

}

结果

在这里插入图片描述

锁粒度

只有一个对象demo3,在这个实例内,incr()方法是加锁同步的,不会有线程不安全的情况

错误演示

public class AtomicDemo3 {

    private static int count = 0;

    public synchronized void incr() {
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        count++;
    }

    public static void main(String[] args) throws InterruptedException {
        AtomicDemo3 demo3 = new AtomicDemo3();
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> {
                new AtomicDemo3().incr();
            }).start();
        }
        Thread.sleep(5000);
        System.out.println(count);
    }

}

结果:
在这里插入图片描述
这里每开启一个Thread线程,就会新new一个实例,在不同实例内,方法还是不安全的

2.2修饰静态方法

对类的静态方法加锁

    public synchronized static void demo3() {
        //TODO
    }

和2.1例子的区别只是
方法前增加了static

    public synchronized static void incr() {
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        count++;
    }

几种调用方式结果一样:
在这里插入图片描述

锁粒度

全局级别和2.3.1 的加锁方式等价

2.3修饰代码块

对类的方法中代码块加锁

2.3.1 全局级别

    public void demo4() {
        //TODO
        synchronized (SyncDemo.class) {
            //保护存在线程安全的代码
        }
        //TODO
    }

锁粒度

这种类型的加锁和synchronized (obj)中的obj有关系,SyncDemo.class存放在方法区中
在这里插入图片描述
所以是全局级别,但是在代码块中可以进行粒度的控制(只对存在线程安全的代码进行加锁)

2.3.2 实例内

    public void demo4() {
        //TODO
        synchronized (this) {
            //保护存在线程安全的代码
        }
        //TODO
    }

this指向这个类的实例

锁粒度

在这个实例内,demo4()方法是加锁同步的

2.3.3 lock的实例内

public class SyncDemo {
    Object lock = new Object;
        public void demo2() {
        //TODO
        synchronized (lock) {
            //保护存在线程安全的代码
        }
        //TODO
    }
 }

lock对象,看这个对象的使用范围

锁粒度

在这个实例内,demo2()方法是加锁同步的

2.3.4 lock的跨实例

public class AtomicDemo4 {

    Object lock;

    public AtomicDemo4(Object lock) {
        this.lock = lock;
    }

    public void demo2() {
        //TODO
        synchronized (lock) {
            //保护存在线程安全的代码
        }
        //TODO
    }

    public static void main(String[] args) {
        Object lock = new Object();
        SyncDemo sd1 = new SyncDemo(lock);
        SyncDemo sd2 = new SyncDemo(lock);
        new Thread(() -> {
            sd1.demo2();
        }).start();
        new Thread(() -> {
            sd2.demo2();
        }).start();
    }
}

锁粒度

在sd1 和sd2两个实例内,demo2()方法是加锁同步的

3.参考

腾讯课堂->咕泡学院->mic老师->并发编程基础

4.系列链接

上一篇:Java并发编程|第二篇:线程生命周期
下一篇:Java并发编程|第四篇:synchronized锁升级

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值