线程安全之《Sychronized的八锁案例》

案例体现在一下几点上:

1. 作用于实例方法,当前实例加锁,进入同步代码前要获得当前实例的锁;

2. 作用于代码块,对括号里配置的对象加锁。

3. 作用于静态方法,当前类加锁,进去同步代码前要获得当前类对象的锁;

案例一:

java 复制代码
public class Dog {
        public synchronized  void eat(){
            System.out.println("-------------小狗----------吃吃吃");
        }
        public synchronized void drink(){
            System.out.println("--------------小狗---------喝喝喝");
        }
}



public class SychronIzedTest1 {
//    sychronized 案例     锁对象
    
    public static void main(String[] args) {
            Dog dog = new Dog();
            new Thread(dog::eat, "线程1").start();
//            暂停200ms
        try {
            TimeUnit.MICROSECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(dog::drink, "线程2").start();
    }
    
}

执行结果:

-------------小狗----------吃吃吃

--------------小狗---------喝喝喝

案例二:

java 复制代码
public class Dog {
        public synchronized  void eat(){
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("-------------小狗----------吃吃吃");
        }
        public synchronized void drink(){
            System.out.println("--------------小狗---------喝喝喝");
        }
}
public class SychronIzedTest1 {
//    sychronized 案例     锁对象
    
    public static void main(String[] args) {
            Dog dog = new Dog();
            new Thread(dog::eat, "线程1").start();
//            暂停200ms
        try {
            TimeUnit.MICROSECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(dog::drink, "线程2").start();
    }
    
}

执行结果:

-------------小狗----------吃吃吃

--------------小狗---------喝喝喝


总结案例1和案例二:

案例一和案例二执行结果是一样的,区别是在案例二上的Dog实体上加了

复制代码
try {
    TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
    e.printStackTrace();
}

猜测加了睡眠,线程1就可以先睡眠三秒,同时线程2执行drink()方法,所以想象的结果是

--------------小狗---------喝喝喝

-------------小狗----------吃吃吃
实际执行结果是

-------------小狗----------吃吃吃

--------------小狗---------喝喝喝

代码执行流程:

  1. 线程1调用dog.eat()方法,获取到dog对象的锁
  2. eat()方法内部执行TimeUnit.SECONDS.sleep(3),但锁并未释放
  3. 线程1继续执行,输出"-------------小狗----------吃吃吃"
  4. 线程2在200微秒后启动,尝试调用dog.drink(),但由于线程1仍在执行(还有约2.8秒才能完成),线程2需要等待获取锁。线程2会一直阻塞直到线程1释放锁

重要知识点:

++synchronized关键字修饰实例方法时,锁住的是当前实例对象(this)。同一时间只有一个线程可以执行该实例的synchronized方法++


案例三:

java 复制代码
public class Dog {
        public synchronized  void eat(){
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("-------------小狗----------吃吃吃");
        }
        public synchronized void drink(){
            System.out.println("--------------小狗---------喝喝喝");
        }

        public void jump(){
            System.out.println("-------------小狗----------跳跳跳");
        }
}


public class SychronIzedTest1 {
//    sychronized 案例     锁对象

    public static void main(String[] args) {
            Dog dog = new Dog();
            new Thread(dog::eat, "线程1").start();
//            暂停200ms
        try {
            TimeUnit.MICROSECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(dog::jump, "线程2").start();
    }

}

区别:

复制代码
增加了一个普通方法:
public void jump(){
    System.out.println("-------------小狗----------跳跳跳");
}

执行结果:

-------------小狗----------跳跳跳

-------------小狗----------吃吃吃

得出结论:

普通方法与同步锁无关


案例四:

java 复制代码
public class Dog {
        public synchronized  void eat(){
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("-------------小狗----------吃吃吃");
        }
        public synchronized void drink(){
            System.out.println("--------------小狗---------喝喝喝");
        }
        public void jump(){
            System.out.println("-------------小狗----------跳跳跳");
        }
}


public class SychronIzedTest1 {
//    sychronized 案例     锁对象

    public static void main(String[] args) {
        Dog dog1 = new Dog();
        Dog dog2 = new Dog();
        new Thread(dog1::eat, "线程1").start();
//            暂停200ms
        try {
            TimeUnit.MICROSECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(dog2::drink, "线程2").start();
    }

}

区别:

从一只狗变成两只狗,并将两只狗分别两个线程执行,以前是一只狗两个线程。

执行结果:

--------------小狗---------喝喝喝

-------------小狗----------吃吃吃

得出结论:

**由于使用的是不同的对象实例,两个线程实际上不会产生竞争,因为它们锁定的是不同的对象。**不是一把锁了互不影响。


案例五:

java 复制代码
public class Dog {
        public static synchronized  void eat(){
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("-------------小狗----------吃吃吃");
        }
        public static synchronized void drink(){
            System.out.println("--------------小狗---------喝喝喝");
        }
        public void jump(){
            System.out.println("-------------小狗----------跳跳跳");
        }
}

public class SychronIzedTest1 {
//    sychronized 案例     锁对象

    public static void main(String[] args) {
//        Dog dog1 = new Dog();
//        Dog dog2 = new Dog();
        new Thread(Dog::eat, "线程1").start();
//            暂停200ms
        try {
            TimeUnit.MICROSECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(Dog::drink, "线程2").start();
    }

}

区别:

两个静态实例方法,一个dog对象

执行结果:

-------------小狗----------吃吃吃

--------------小狗---------喝喝喝

案例六:

java 复制代码
public class Dog {
        public static synchronized  void eat(){
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("-------------小狗----------吃吃吃");
        }
        public static synchronized void drink(){
            System.out.println("--------------小狗---------喝喝喝");
        }
        public void jump(){
            System.out.println("-------------小狗----------跳跳跳");
        }
}



public class SychronIzedTest1 {
//    sychronized 案例     锁对象

    public static void main(String[] args) {
        Dog dog1 = new Dog();
        Dog dog2 = new Dog();
        new Thread(()->dog1.eat(), "线程1").start();
//            暂停200ms
        try {
            TimeUnit.MICROSECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->dog2.drink(), "线程2").start();
    }

}

区别:

两个静态实例方法,两个dog对象

执行结果:

-------------小狗----------吃吃吃

--------------小狗---------喝喝喝


总结案例五和案例六:

对于静态同步方法,锁定的是当前class类


案例七:

java 复制代码
public class Dog {
        public static synchronized  void eat(){
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("-------------小狗----------吃吃吃");
        }
        public  synchronized void drink(){
            System.out.println("--------------小狗---------喝喝喝");
        }
        public void jump(){
            System.out.println("-------------小狗----------跳跳跳");
        }
}


public class SychronIzedTest1 {
//    sychronized 案例     锁对象

    public static void main(String[] args) {
        Dog dog1 = new Dog();
        new Thread(()->dog1.eat(), "线程1").start();
//            暂停200ms
        try {
            TimeUnit.MICROSECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->dog1.drink(), "线程2").start();
    }

}

区别:

一个普通同步方法,一个静态同步方法,一个dog对象

执行结果:

--------------小狗---------喝喝喝

-------------小狗----------吃吃吃


案例八:

java 复制代码
public class Dog {
        public static synchronized  void eat(){
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("-------------小狗----------吃吃吃");
        }
        public  synchronized void drink(){
            System.out.println("--------------小狗---------喝喝喝");
        }
        public void jump(){
            System.out.println("-------------小狗----------跳跳跳");
        }
}




public class SychronIzedTest1 {
//    sychronized 案例     锁对象

    public static void main(String[] args) {
        Dog dog1 = new Dog();
        Dog dog2 = new Dog();
        new Thread(()->dog1.eat(), "线程1").start();
//            暂停200ms
        try {
            TimeUnit.MICROSECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->dog2.drink(), "线程2").start();
    }

}

区别:

一个普通同步方法,一个静态同步方法,两个对象

执行结果:

--------------小狗---------喝喝喝

-------------小狗----------吃吃吃


总结案例七和案例八:

多线程下普通同步方法和静态同步方法互不影响。

1.锁对象不同
普通同步方法(如 drink()):锁定的是实例对象,即调用该方法的具体对象实例
静态同步方法(如 eat()):锁定的是类对象,即该类的 Class 对象

2.互不影响的原因

由于锁的对象不同,普通同步方法和静态同步方法确实互不影响

多个线程可以同时执行同一类中的普通同步方法和静态同步方法


总结:

  • 对于普通同步方法,锁的是当前实例对象,通常指this,所有的普通同步方法用的 都是同一把锁------实例对象本身
  • 对于静态同步方法,锁的是当前类的Class对象
  • 对于同步方法块,锁的是 synchronized 括号内的对象
相关推荐
老K的Java兵器库17 小时前
集合性能基准测试报告:ArrayList vs LinkedList、HashMap vs TreeMap、并发 Map 四兄弟
java·开发语言
Knight_AL17 小时前
如何解决 Jacob 与 Tomcat 类加载问题:深入分析 Tomcat 类加载机制与 JVM 双亲委派机制
java·jvm·tomcat
枫叶丹417 小时前
【Qt开发】多元素类控件(二)-> QTableWidget
开发语言·qt
bin915317 小时前
当AI开始‘映射‘用户数据:初级Python开发者的创意‘高阶函数‘如何避免被‘化简‘?—— 老码农的函数式幽默
开发语言·人工智能·python·工具·ai工具
哲学七17 小时前
Springboot3.5.x版本引入javaCv相关库版本问题以及精简引入包
java·ffmpeg
Aqua Cheng.18 小时前
代码随想录第七天|哈希表part02--454.四数相加II、383. 赎金信、15. 三数之和、18. 四数之和
java·数据结构·算法·散列表
Nebula_g18 小时前
Java哈希表入门详解(Hash)
java·开发语言·学习·算法·哈希算法·初学者
努力努力再努力wz18 小时前
【C++进阶系列】:万字详解unordered_set和unordered_map,带你手搓一个哈希表!(附模拟实现unordered_set和unordered_map的源码)
java·linux·开发语言·数据结构·数据库·c++·散列表
励志不掉头发的内向程序员18 小时前
【STL库】哈希表的原理 | 哈希表模拟实现
开发语言·c++·学习·散列表