文章目录
前言
跟着狂神学JUC,结合狂神提出的关于锁的几点易混淆的知识方面,结合自己的理解,写一些自己的心得体会。
狂神说的八锁
狂神代码和理解分析
1、synchronized锁方法的调用者(单资源)
当一个资源
调用两个加锁的方法,其打印信息看下列代码案例:
package demo3;
import java.util.concurrent.TimeUnit;
public class Test01 {
public static void main(String[] args) throws InterruptedException {
// 同一个资源
Computer computer = new Computer();
new Thread(()->{
computer.watch();},"A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
computer.study();},"B").start();
}
}
class Computer{
public synchronized void watch(){
System.out.println("看电影。。。。");
}
public synchronized void study(){
System.out.println("学习。。。");
}
}
如果说是因为调用顺序问题,接下来看下列代码:
package demo3;
import java.util.concurrent.TimeUnit;
public class Test02 {
public static void main(String[] args) throws InterruptedException {
Computer2 computer = new Computer2();
new Thread(()->{
computer.watch();},"A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
computer.study();},"B").start();
}
}
class Computer2{
// synchronized 锁的对象是方法的调用者
// 两个方法,由于被调用,是同一个资源,导致谁先获取到锁,谁先执行!
public synchronized void watch(){
// 延迟 4秒
try {
TimeUnit.SECONDS.sleep(4);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("看电影。。。。");
}
public synchronized void study(){
System.out.println("学习。。。");
}
}
[问:]
即使在watch()中加了延迟,针对两个不同的线程,打印出的信息为什么依旧存在顺序?
这里的操作者 computer 是同一资源!
1、由于线程A随系统启动,立刻就能调用watch();
2、由于线程A调用的watch()方法存在延迟4s等待。此时虽然是主线程1s等待结束,但线程B依旧还在等A释放锁!
[总结:]
synchronized 锁的对象是方法的调用者!
2、同时存在锁和不锁的打印情况(单资源)
一个资源
中,存在加锁操作方法和不加锁的普通方法,其代码逻辑如下所示:
package demo3;
import java.util.concurrent.TimeUnit;
public class Test03 {
public static void main(String[] args) throws InterruptedException {
Computer3 computer = new Computer3();
new Thread(()->{
computer.watch();},"A").start();
TimeUnit.SECONDS.sleep(1);
// 调用了其中无锁的方法
new Thread(()->{
computer.talk();},"B").start();
}
}
class Computer3{
// synchronized 锁的对象是方法的调用者
// 两个方法,由于被调用,是同一个资源,导致谁先获取到锁,谁先执行!
public synchronized void watch(){
// 延迟 4秒
try {
TimeUnit.SECONDS.sleep(4);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("看电影。。。。");
}
public synchronized void study(){
System.out.println("学习。。。");
}
// 没有锁,不是同步方法,不受锁的影响!
public void talk(){
System.out.println("聊天。。。。");
}
}
[结论:]
由于存在无锁的普通方法,其不受锁的影响!
3、多资源调用不同加锁方法
多个资源对象
调用不同的加锁方法,逻辑如下所示:
package demo3;
import java.util.concurrent.TimeUnit;
public class Test04 {
public static void main(String[] args) throws InterruptedException {
// 两个资源
Computer4 computer1 = new Computer4();
Computer4 computer2 = new Computer4();
new Thread(()->{
computer1.watch();},"A").start();
TimeUnit.SECONDS.sleep(1);
// 调用了其中无锁的方法
new Thread(()->{
computer2.study();},"B").start();
}
}
class Computer4{
// synchronized 锁的对象是方法的调用者
// 两个方法,由于被调用,是同一个资源,导致谁先获取到锁,谁先执行!
public synchronized void watch(){
// 延迟 4秒
try {
TimeUnit.SECONDS.sleep(4);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("看电影。。。。");
}
public synchronized void study(){
System.out.println("学习。。。");
}
}
[总结:]
1、两个不同的资源,就是两个不同的调用者!
2、synchronized 锁的是同一个调用者,即针对相同资源的加锁!
4、static修饰带synchronized的方法(单资源)
采取一个实例化对象资源,分别开启不同的线程,调用不同的static
synchronized
方法。
package demo3;
import java.util.concurrent.TimeUnit;
/**
* 一个对象,调用两个不同的static 同步方法
*/
public class Test05 {
public static void main(String[] args) throws InterruptedException {
Computer5 computer = new Computer5();
new Thread(()->{
computer.watch();},"A").start();
TimeUnit.SECONDS.sleep(1);
// 调用了其中无锁的方法
new Thread(()->{
computer.study();},"B").start();
}
}
/**
* 两个静态同步方法
*/
class Computer5{
// synchronized 锁的对象是方法的调用者
// 两个方法,由于被调用,是同一个资源,导致谁先获取到锁,谁先执行!
public static synchronized void watch(){
// 延迟 4秒
try {
TimeUnit.SECONDS.sleep(4);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("看电影。。。。");
}
public static synchronized void study(){
System.out.println("学习。。。");
}
}
5、多资源调用static修饰的synchronized方法
在4
中,是采取的单资源调用,其中的锁是针对一个调用者而言,所以和1
中的结果是一致的。
但是如果是两个资源调用static
synchronized
修饰的方法呢?
package demo3;
import java.util.concurrent.TimeUnit;
/**
* 两个对象,调用两个不同的static 同步方法
*/
public class Test06 {
public static void main(String[] args) throws InterruptedException {
Computer6 computer1 = new Computer6();
Computer6 computer2 = new Computer6();
// 1、两个资源
// 2、但是同步方法为 static synchronized
// 3、static 是静态资源,程序一加载,就保存至jvm中
// 4、static synchronized 加锁的是class文件,并非是锁的调用者
// 5、两个不同的资源类,但是class唯一,不同的对象其编译后的文件是同一个!!
// 6、导致不同对象 static synchronized 依旧保证顺序!
new Thread(()->{
computer1.watch();},"A").start();
TimeUnit.SECONDS.sleep(1);
// 调用了其中无锁的方法
new Thread(()->{
computer2.study();},"B").start();
}
}
/**
* 两个静态同步方法
*/
class Computer6{
// synchronized 锁的对象是方法的调用者
// 两个方法,由于被调用,是同一个资源,导致谁先获取到锁,谁先执行!
// static 表示静态资源,在类初次加载的时候,就会产生放入至jvm内存中
// static synchronized 这个静态类型的锁,锁的是class!
public static synchronized void watch(){
// 延迟 4秒
try {
TimeUnit.SECONDS.sleep(4);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("看电影。。。。");
}
public static synchronized void study(){
System.out.println("学习。。。");
}
}
此时的运行结果,和3
的结果却不相同!!
[问题分析:]
1、类中,static 修饰的方法、变量等属于静态资源类。在程序一加载的时候,就会存在JVM的内存中。
2、此时的 static synchronized 锁的是class对象,而并非是针对资源加锁!
3、class 全局唯一!两个对象编译后的class却都是相同的一个!
6、同时存在static锁和正常锁(单资源)
同一个资源调用携带static synchronized
和普通的synchronized
方法。
package demo3;
import java.util.concurrent.TimeUnit;
/**
* 两个对象,调用两个不同的static 同步方法
*/
public class Test07 {
public static void main(String[] args) throws InterruptedException {
Computer7 computer = new Computer7();
// 1、两个资源
// 2、但是同步方法为 static synchronized
// 3、static 是静态资源,程序一加载,就保存至jvm中
// 4、static synchronized 加锁的是class文件,并非是锁的调用者
// 5、两个不同的资源类,但是class唯一,不同的对象其编译后的文件是同一个!!
new Thread(()->{
computer.watch();},"A").start();
TimeUnit.SECONDS.sleep(1);
// 调用了其中无锁的方法
new Thread(()->{
computer.study();},"B").start();
}
}
/**
* 一个静态同步方法
*/
class Computer7{
// synchronized 锁的对象是方法的调用者
// 两个方法,由于被调用,是同一个资源,导致谁先获取到锁,谁先执行!
// static 表示静态资源,在类初次加载的时候,就会产生放入至jvm内存中
// static synchronized 这个静态类型的锁,锁的是class!
public static synchronized void watch(){
// 延迟 4秒
try {
TimeUnit.SECONDS.sleep(4);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("看电影。。。。");
}
// 这个只是一个普通的锁
public synchronized void study(){
System.out.println("学习。。。");
}
}
其执行后的日志打印为:
这个却和预期的相反,就好像这是两个锁?
[问题分析:]
1、static 表示静态,类一加载,就会存在!
2、static synchronized 表示针对class文件加锁。
3、不携带 static 修饰的 synchronized,加锁的是调用者!
4、static synchronized 是class 加锁,单一的 synchronized 只是调用方加锁,这两个是不同的锁
!
7、多资源消费同时存在static锁和正常锁
在单资源
中,由于static synchronized (class锁)
和synchronized (调用者锁)
是两个不同的锁,所以开辟两个线程,不会影响其执行顺序!
但是此时如果是两个线程分别调用两个不同的带锁方法呢?
package demo3;
import java.util.concurrent.TimeUnit;
/**
* 两个对象,调用两个不同的static 同步方法
*/
public class Test08 {
public static void main(String[] args) throws InterruptedException {
Computer8 computer1 = new Computer8();
Computer8 computer2 = new Computer8();
// 1、两个资源
// 2、但是同步方法为 static synchronized
// 3、static 是静态资源,程序一加载,就保存至jvm中
// 4、static synchronized 加锁的是class文件,并非是锁的调用者
// 5、两个不同的资源类,但是class唯一,不同的对象其编译后的文件是同一个!!
new Thread(()->{
computer1.watch();},"A").start();
TimeUnit.SECONDS.sleep(1);
// 调用了其中无锁的方法
new Thread(()->{
computer2.study();},"B").start();
}
}
/**
* 一个静态同步方法
*/
class Computer8{
// synchronized 锁的对象是方法的调用者
// 两个方法,由于被调用,是同一个资源,导致谁先获取到锁,谁先执行!
// static 表示静态资源,在类初次加载的时候,就会产生放入至jvm内存中
// static synchronized 这个静态类型的锁,锁的是class!
public static synchronized void watch(){
// 延迟 4秒
try {
TimeUnit.SECONDS.sleep(4);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("看电影。。。。");
}
// 这个只是一个普通的锁
public synchronized void study(){
System.out.println("学习。。。");
}
}
两个对象互不相关。
带static的synchronized锁的是class文件,任何.java类
都只会有一个.class文件
;
不携带static的synchronized,只是一个调用者的锁!