前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >synchronized锁住了什么?

synchronized锁住了什么?

作者头像
naget
发布2019-07-03 15:40:39
8600
发布2019-07-03 15:40:39
举报
文章被收录于专栏:VegoutVegout

多线程环境中,对于共享变量的访问一定需要进行正确的管理才能保证代码的正确执行,也就是保证线程安全。而加synchronized关键关键字无疑是个简单的办法,synchronized是java提供的一个关键字,给代码块或者方法加上这个关键字就可以保证一个线程开始执行被synchronized修饰的方法或代码块时,首先需要获得这个对象的内置锁,执行完被保护的代码之后再释放这个锁,获取锁和释放所都是Jvm来完成的,我们需要做的只是给需要保证线程安全的方法加一个关键词。但不同的情况,“这个对象”又有不同的指向。 为了说明每种情况,这里给出一个示例:

代码语言:javascript
复制
public class LockTest {
    static int count;
    public  synchronized void InstanceAdd(){
        count++;
    }
    public static synchronized void staticAdd(){
    count++;}
    public  void  blockAdd(){
        synchronized (this){
            count++;
        }
    }
    public static void main(String[] args) {
        LockTest lockTest1 = new LockTest();
        LockTest lockTest2 = new LockTest();
           Thread thread1= new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i=0;i<1000;i++){
                        lockTest1.staticAdd();
                       // lockTest1.InstanceAdd();
                       // lockTest1.blockAdd();
                    }
                }
            });
            Thread thread2=new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i=0;i<1000;i++){
                        lockTest2.staticAdd();
                      //   lockTest2.blockAdd();
                       // lockTest2.InstanceAdd();
                    }
                }
            });
            thread1.start();
            thread2.start();
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("count="+count);
    }
}

这个示例,有一个类变量count,然后三个方法实现同一个逻辑,就是count++,这三个方法都实现了同步,只不过有些不同,分别是类方法的同步,实例方法的同步,代码块的同步。main函数实例化了两个LockTest对象,启动了两个线程,每个线程通过调用这三种方法的其中之一进行1000次count++,如果线程安全的话,最终打印出的count应该是2000。

  • synchronized作用于代码块时 每个run方法中我们都调用对应对象的blockAdd方法,我们会发现count并没有加到2000。

这是因为此时thread1获得的是lockTest1的内置锁,thread2获得了lockTest2的内置锁,这两个线程对count进行的是肆无忌惮的操作,没有达到同步的效果,所以当synchronized作用于代码块时,他锁住的就是synchronized()括号中的对象,执行这段代码需要获得的就是这个对象的内置锁。

  • synchronized作用于实例方法时 每个run方法中我们都调用对应对象的InstanceAdd方法,我们会发现count仍然并没有加到2000,这个原因跟作用于代码块时是类似的,执行受保护代码的条件是获得此刻对象的内置锁,而很明显,两个线程操作的是两个不同的对象,也就获得了不同对象的内置锁。
  • synchronized作用于类方法时 每个run方法中我们都调用对应对象的staticAdd方法,很开心count终于加到2000了。此时,我们的两个线程仍然使用了不同的对象来进行count++,但为什么线程安全了呢?能否保证线程安全,就看synchronized锁住的是不是同一个对象,当一个类方法被synchronized修饰时,需要获取的内置锁所在的对象不是任何一个实例对象而是类对象。所以,我们相当于加了一个全局锁,staticAdd就成为了一个安全的操作了。

除了这三种情况还有很重要的一点,就是我们题目所说,synchronized到底锁住了什么?是受保护代码还是对象?是对象。java中每个对象都有一个内置锁,而“锁住”是什么意思?就是获得了这个对象的内置锁。但一个线程获得了某个对象的内置锁之后,其他线程还能访问这个对象吗?是可以的。 我们给这个类新加一个方法

代码语言:javascript
复制
    public void add(){
        count++;
    }

其中一个线程调用同步方法,另一个线程调用这个方法。并且我们两个线程使用同一个对象,lockTest1或lockTest2。我们会发现,程序正常执行,只是count没有加到2000。对与调用同步方法的线程来说,他获取了相应对象的内置锁,对与调用add这个新方法的线程来说,他并没有去获得任何锁,但前一个线程对对象锁的获取并不能影响后一个线程对对象的访问,于是后一个线程还是可以直接调用这个add方法。所以说,锁机制更像是一种约定,约定我们进行某个操作之前必须去获取某个锁,从而来完成多线程的安全操作。通过合理运用synchronized我们可以做到线程安全,但无疑的是,其中的代码对与多个线程来说只能是排队执行,大大降低了应用程序的性能,锁的获得和释放本身也是要消耗资源的,于是就有了对锁的优化,当然这就是后话了。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-06-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Vegout 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档