天天看点

关于synchronized的一点使用总结

前言:文章为作者原创,转发请注明出处。文中有不妥描述欢迎大家指正,疑点欢迎大家共同讨论。

情景一

一个类,在每个线程中都自己实例化使用(多实例),类内不存在任何静态成员方法和静态成员变量,此时synchronized关键字无论修饰成员方法还是锁代码块,不会有任何效果。

关于synchronized的一点使用总结
关于synchronized的一点使用总结

 分析:每个线程自己独有类实例,由于成员方法和成员变量非静态,其他线程不可共享,此时加锁其实是偏向锁,偏向锁其实不上锁,和不加锁一个效果。

情景二

一个类,在每个线程中都自己实例化使用(多实例),类内存在静态成员变量,对操作该静态变量的非静态成员方法使用synchronized修饰,这种情况下对静态成员变量的操作不安全。

关于synchronized的一点使用总结

 分析:由于被锁的成员方法并非静态方法,方法是自己私有的也只会被自己调用,加锁和不加锁是一个效果。同一时刻多个线程都可以调用线程内类实例的该方法,不受限制,如果多个线程同时调用该方法时静态变量的值相同,然后同时各自执行方法,结果就是多个线程干了相同的操作,原本应该叠加的结果没有叠加。

情景三

这个情景是在情景二的基础上对静态成员变量使用volatile修饰,使其变化能被其他线程及时可见,此时其实也不能保证线程安全。

关于synchronized的一点使用总结

分析:和情景二一个道理,它并不能阻止同一时刻多个线程读取到同一个静态变量值和同时去执行+1操作。 

情景四

一个类,在每个线程中都自己实例化使用(多实例),锁定非静态方法内的代码块(将静态成员变量作为锁对象),此时就能保证线程安全。

关于synchronized的一点使用总结

 分析:此时多线程同时到达代码块时,都要先去申请锁,由于锁对象是静态对象,是全局唯一存在的,都是向它申请锁,而且锁对象同一时刻只会把锁给到一个线程,其他线程想要得到锁必需得等其他线程释放这个锁,于是,并发通过锁竞争,变成了串行化,所有线程依次执行,同一时刻只有获取锁的一条线程执行+1操作。

ps:提一下类锁这个词,“synchronized(静态成员变量)” 这种用法和 “synchronized修饰静态方法” 这两种锁方式很多人称之为类锁,从字面意思理解,好像是对类象的锁定,同一时刻只能有一个线程操作该类,也就是说同一时刻只会存在该类的一个实例对象,很多人也这么理解。其实不是,实际是同一时刻可以存在该类的多个实例对象,并且线程内类实例的非锁定方法(或代码块)的执行不受其他线程影响(可以自己试试)。所以类锁这个词严格意义上来说并不能叫类锁,应该叫共享资源锁或者全局资源锁或者静态资源锁,这两种锁其实限制的是全局唯一的资源同一时刻只能给一个线程拥有操作权限。

情景五

一个类,在每个线程中都自己实例化使用(多实例),synchronized修饰静态成员方法。此时也能保证线程安全。

关于synchronized的一点使用总结

情景六

单例(spring容器中bean实例默认就是单实例)synchronized修饰成员方法(非静态成员方法),此时多线程调用方法操作也是安全的(自然地,静态成员方法也是安全的)。

关于synchronized的一点使用总结

 分析:由于类对象是单例存在,多线程时操作的都是同一个实例的同一个方法对象,此时对于所有线程来说这个方法就是全局资源,作用在这个方法上的锁自然对所有线程起到限制作用。

情景七

单例synchronized使用成员变量(非静态成员变量)作为锁对象锁代码块,此时多线程调用方法操作也是安全的(自然地,使用静态成员变量作为锁对象也是安全的)。

关于synchronized的一点使用总结

 分析:和情景七差不多的道理,成员变量也是全局资源,对所有线程也起到限制作用。