synchronized锁的是什么?

1.synchronized是什么

synchronized是java里的一个关键字,可以用来给对象和方法或者代码块加锁,当它锁定一个方法或者代码块的时候,同一时刻最多只有一条线程执行这段代码

synchronized修饰的对象:

  • 修饰方法,被修饰的方法被称为同步方法
  • 修饰代码块,被修饰的代码块被称为同步代码块

2.synchronized锁的是什么

未加锁状态下线程执行:

// 未加锁的普通方法测试
public class TestMethod {
    public static void main(String[] args) {
        Date date = new Date();
        
        // 开启一个A线程执行 fun1() 方法
        new Thread(()->{
            try {
                date.fun1();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();

		// 主线程休眠1秒
        Thread.sleep(1000);

        // 开启一个B线程执行 fun2() 方法
        new Thread(()->{
            try {
                date.fun2();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"B").start();
    }
}
class Date{
    // 未加synchronized修饰
    public void fun1() throws InterruptedException {
        // 延迟3秒后继续运行
        Thread.sleep(3000);
        System.out.println("fun1......");
    }
    // 未加synchronized修饰
    public void fun2() throws InterruptedException {
        // 直接运行
        System.out.println("fun2......");
    }
}

执行结果:
在这里插入图片描述
显而易见,fun2()由于没有延迟3秒,于是fun2()先打印

1、修饰普通方法

1.1、两个普通方法都加锁

当锁的是普通方法(非静态方法)时,锁的是方法的调用者,也就是下面代码中date这个对象

// 两个普通方法都加锁的测试
public class TestMethod2 {
    public static void main(String[] args) {
        Date2 date = new Date2();

        // 开启一个A线程执行 fun1() 方法
        new Thread(()->{
            try {
                date.fun1();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();

		// 主线程休眠1秒
        Thread.sleep(1000);

        // 开启一个B线程执行 fun2() 方法
        new Thread(()->{
            try {
                date.fun2();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"B").start();
    }
}
class Date2{
    // 加synchronized修饰
    public synchronized void fun1() throws InterruptedException {
        // 延迟3秒后继续运行
        Thread.sleep(3000);
        System.out.println("fun1......");
    }
    // 加synchronized修饰
    public synchronized void fun2() throws InterruptedException {
        // 直接运行
        System.out.println("fun2......");
    }
}

执行结果:
在这里插入图片描述
从结果可以看出,由于锁的是方法的调用者,也就是date这个对象,所以A线程先来执行fun1()方法的时候锁定date,导致属于date里面的fun2()方法也被锁定,B线程必须等A线程执行完fun1()方法之后,A线程释放锁,B线程才能重新锁定date,并执行date里的fun2()方法

1.2、一个普通方法加锁,一个普通方法未加锁

普通方法加锁,锁的是调用者,而不加锁的普通方法执行是不需要锁的

// 一个普通方法加锁,一个普通方法未加锁的测试
public class TestMethod2 {
    public static void main(String[] args) throws InterruptedException {
        Date2 date = new Date2();

        // 开启一个A线程执行 fun1() 方法
        new Thread(()->{
            try {
                date.fun1();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();
        
        // 主线程休眠1秒
        Thread.sleep(1000);

        // 开启一个B线程执行 fun2() 方法
        new Thread(()->{
            try {
                date.fun2();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"B").start();
    }
}
class Date2{
    // 加synchronized修饰
    public synchronized void fun1() throws InterruptedException {
        // 延迟3秒后继续运行
        Thread.sleep(3000);
        System.out.println("fun1......");
    }
    // 普通方法
    public  void fun2() throws InterruptedException {
        // 直接运行
        System.out.println("fun2......");
    }
}

执行结果:
在这里插入图片描述
从结果来看是fun2()先输出,这是因为fun1()有线程同步,所以fun1()的执行需要锁定date这个对象,而fun2()不是线程同步的,所以fun2()的执行不需要锁定对象就能执行,所以fun2()的执行不需要等fun1()释放锁住的对象,所以fun2()先输出

1.3、两个对象分别执行两个都加锁的普通方法

两个不同的对象,锁的是方法的调用者,所以两个对象分别执行两个普通方法的话,互不干扰

// 两个对象分别执行两个都加锁的普通方法的测试
public class TestMethod2 {
    public static void main(String[] args) throws InterruptedException {
        Date2 date1 = new Date2();
        Date2 date2 = new Date2();

        // 开启一个A线程执行 fun1() 方法
        new Thread(()->{
            try {
                date1.fun1();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();
        
        // 主线程休眠1秒
        Thread.sleep(1000);

        // 开启一个B线程执行 fun2() 方法
        new Thread(()->{
            try {
                date2.fun2();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"B").start();
    }
}
class Date2{
    // 加synchronized修饰
    public synchronized void fun1() throws InterruptedException {
        // 延迟3秒后继续运行
        Thread.sleep(3000);
        System.out.println("fun1......");
    }
    // 加synchronized修饰
    public  synchronized void fun2() throws InterruptedException {
        // 直接运行
        System.out.println("fun2......");
    }
}

执行结果:
在这里插入图片描述
从结果看出,fun2()先输出,因为当date1执行fun1()方法的时候,锁的是date1date2执行fun2()的时候,锁的是date2,锁的是两个不同的对象,不存在竞争,所以fun2()先输出

2、修饰静态方法

2.1、两个对象分别执行两个都加锁的静态方法

静态方法锁定的是,不是对象,所以不管多少个对象锁的都是一个类

// 两个对象分别执行两个都加锁的静态方法的测试
public class TestMethod2 {
    public static void main(String[] args) throws InterruptedException {
        Date2 date1 = new Date2();
        Date2 date2 = new Date2();

        // 开启一个A线程执行 fun1() 方法
        new Thread(()->{
            try {
                date1.fun1();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();
        
        // 主线程休眠1秒
        Thread.sleep(1000);

        // 开启一个B线程执行 fun2() 方法
        new Thread(()->{
            try {
                date2.fun2();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"B").start();
    }
}
class Date2{
    // 加synchronized修饰的静态方法
    public synchronized static void fun1() throws InterruptedException {
        // 延迟3秒后继续运行
        Thread.sleep(3000);
        System.out.println("fun1......");
    }
    // 加synchronized修饰的静态方法
    public  synchronized static void fun2() throws InterruptedException {
        // 直接运行
        System.out.println("fun2......");
    }
}

执行结果:
在这里插入图片描述
从结果来看,fun1()先输出,因为静态方法锁的是,而类只有一个,所以fun2()需要等待fun1()执行完后释放锁,fun2()才能执行

2.2、两个对象分别执行一个加锁、一个未加锁的静态方法

静态方法加锁锁定的是类,而fun2()未加锁,不需要锁定类就能直接执行

// 两个对象分别执行一个加锁、一个未加锁的静态方法的测试
public class TestMethod2 {
    public static void main(String[] args) throws InterruptedException {
        Date2 date1 = new Date2();
        Date2 date2 = new Date2();

        // 开启一个A线程执行 fun1() 方法
        new Thread(()->{
            try {
                date1.fun1();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();
        
        // 主线程休眠1秒
        Thread.sleep(1000);

        // 开启一个B线程执行 fun2() 方法
        new Thread(()->{
            try {
                date2.fun2();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"B").start();
    }
}
class Date2{
    // 加synchronized修饰
    public synchronized static void fun1() throws InterruptedException {
        // 延迟3秒后继续运行
        Thread.sleep(3000);
        System.out.println("fun1......");
    }
    // 未加synchronized修饰的静态方法
    public  static void fun2() throws InterruptedException {
        // 直接运行
        System.out.println("fun2......");
    }
}

执行结果:
在这里插入图片描述
从结果来看,fun2先输出,这是因为fun1执行的时候锁的是类,而fun2没有加锁,所以fun2无需锁定就可以进行直接执行,也就是说fun2()无需等待fun1()释放锁资源,所以fun2()先输出

3、修饰代码块

未加锁的状态下的测试:

// 未加锁的状态测试
public class TestCode {
    public static void main(String[] args) {
        Date3 date3 = new Date3();
        // 启动五条线程
        for (int i = 0; i < 5; i++) {
            new Thread(()->{
                try {
                    date3.fun();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
class Date3 {
	// 未加锁的状态
    public void fun() throws InterruptedException {
        System.out.println("start......");
        // 休眠1秒
        Thread.sleep(1000);
        System.out.println("end......");
    }
}

执行结果:
在这里插入图片描述
从结果看出,正常情况下,在五条线程执行完start…的时候都会停下等待1秒,然后全部执行完
修饰代码块,锁定的是传入的对象

3.1、代码块加锁,传入this

当加锁的代码块传入的是this的时候,锁的也是方法的调用者,也就是date3这个对象

// 代码块加锁,传入this的测试
public class TestCode {
    public static void main(String[] args) {
        Date3 date3 = new Date3();
        // 启动五条线程
        for (int i = 0; i < 5; i++) {
            new Thread(()->{
                try {
                    date3.fun();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
class Date3 {
    public void fun() throws InterruptedException {
        // 代码块加锁,传入this
        synchronized (this) {
            System.out.println("start......");
            // 休眠1秒
            Thread.sleep(1000);
            System.out.println("end......");
        }

    }
}

执行结果:
在这里插入图片描述
从结果来看,是排队输出的,因为传入的是this,所以锁的是date3这个对象,所以每个线程在执行的时候都在等待上一个线程释放对象(锁),这个线程才能继续执行

3.2、代码块加锁,传入this,五个不同对象

代码块加锁,传入this,锁的方法的调用者,由于是五个不同的对象,所以互不干扰

// 代码块加锁,传入this,五个不同对象的测试
public class TestCode {
    public static void main(String[] args) {
        // 启动五条线程
        for (int i = 0; i < 5; i++) {
        	// 每次循环创建一个新的对象
            Date3 date3 = new Date3();
            new Thread(()->{
                try {
                    date3.fun();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
class Date3 {
    public void fun() throws InterruptedException {
        // 代码块加锁,传入this
        synchronized (this) {
            System.out.println("start......");
            // 休眠1秒
            Thread.sleep(1000);
            System.out.println("end......");
        }

    }
}

执行结果:
在这里插入图片描述
从结果来看,五个线程基本是一起输出的,因为,代码块加锁,传入this,锁的是方法的调用者,所以五个线程之间互不干扰,一起输出

3.3、代码块加锁,传入类(.class),五个不同对象

代码块加锁,当传入的是(.class) 的时候,锁定的自然就是类了

// 代码块加锁,传入类(.class),五个不同对象的测试
public class TestCode {
    public static void main(String[] args) {
        // 启动五条线程
        for (int i = 0; i < 5; i++) {
            Date3 date3 = new Date3();
            new Thread(()->{
                try {
                    date3.fun();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
class Date3 {
    public void fun() throws InterruptedException {
        // 代码块加锁,传入类(.class)
        synchronized (Date3.class) {
            System.out.println("start......");
            // 休眠1秒
            Thread.sleep(1000);
            System.out.println("end......");
        }

    }
}

执行结果:
在这里插入图片描述
从结果来看,线程是一条一条排队输出的,因为当代码块锁中传入的是的时候,锁定的就是类,而类只有一个,所以即使是不同的对象,也必须等待锁的释放

3.4、代码块加锁,传入Integer的对象,五个不同对象

Integer有一个特殊之处,当直接定义Integer而不用构造器,Integer的值是在-128~127之间的情况下,Integer其实都是存在常量池的,所以即使看起来是五个不同的对象那个,但其实是同一个对象

// 代码块加锁,传入Integer的对象,五个不同对象的测试
public class TestCode {
    public static void main(String[] args) {
        // 启动五条线程
        for (int i = 0; i < 5; i++) {
            Date3 date3 = new Date3();
            new Thread(()->{
                try {
                    date3.fun();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
class Date3 {
    public void fun() throws InterruptedException {
        // 代码块加锁,传入普通Integer的对象
        Integer num = 1;
        synchronized (num) {
            System.out.println("start......");
            // 休眠1秒
            Thread.sleep(1000);
            System.out.println("end......");
        }

    }
}

执行结果:
在这里插入图片描述
从结果来看,线程依然是排队输出的,这是因为即使看起来是五条线程中每次都回生成一个新的Integer对象,但是Integer有一个特殊之处,当直接定义Integer而不用构造器,Integer的值是在-128~127之间的情况下,Integer其实都是存在常量池的,所以即使看起来是五个不同的对象那个,但其实是同一个对象。测试的方法也很简单,只需把Integer num = 1;换成Integer num = new Integer(1);或者Integer num = 128就可以看出区别

注意:即使在同步代码块里加一句num++;锁的依然是同一个对象,需要排队

  • 19
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值