浅谈java synchronized 锁的问题

问题:

平时对锁的研究不是很深,只知道它的概念,有对象锁,类锁,方法锁这些,但是实际使用就分不清什么时候锁是生效的,这里略微总结下对象锁和类锁的使用。

一、类锁的使用

第一种情况:同一个方法上加类锁
首先应该明确的是java同一个类只会加载一次,所以在给一个方法中上类锁,无论有多少个线程new了多少个对象去调用这个方法,那么都是排队执行的。

public class SynTest {

    public static void read (String threadName) {
        synchronized (SynTest.class) { //类锁
            System.out.printf("【%s】我是read方法!开始执行....%n", threadName);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.printf("【%s】我是read方法!执行结束!%n", threadName);
        }
    }
}
public class ConvertDemoApplication {

    public static void main(String[] args) {
    	//模拟多个线程同时去调用read方法,观察是否时串行执行的
        new Thread(() -> {
            SynTest.read(Thread.currentThread().getName());
        }, "aaa").start();

        new Thread(() -> {
            SynTest.read(Thread.currentThread().getName());
        }, "bbb").start();

        new Thread(() -> {
            SynTest.read(Thread.currentThread().getName());
        }, "ccc").start();
    }
}        

执行结果: 结论是串行执行的

【aaa】我是read方法!开始执行....
【aaa】我是read方法!执行结束!
【ccc】我是read方法!开始执行....
【ccc】我是read方法!执行结束!
【bbb】我是read方法!开始执行....
【bbb】我是read方法!执行结束!

第二种情况:同一个类中,不同方法中上类锁
同一个类中有多个方法都上了类锁,这时候就是互斥锁,多个线程调用不同的方法时,也需要排队执行,即调用A结束后才能调用B;

public class SynTest {
	
	//同一个类中的read方法
    public static void read (String name) {
        synchronized (SynTest.class) {
            System.out.printf("【%s】我是read方法!开始执行....%n", name);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.printf("【%s】我是read方法!执行结束!%n", name);
        }
    }
	//同一个类中的write方法
    public static void write (String name) {
        synchronized (SynTest.class) {	//类锁
            System.out.printf("【%s】我是write方法!开始执行....%n", name);
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.printf("【%s】我是write方法!执行结束!%n", name);
        }
    }
}
public class ConvertDemoApplication {


    private static int index = 0;

    public static void main(String[] args) {

        //下面是调用read方法
        new Thread(() -> {
            SynTest.read(Thread.currentThread().getName());
        }, "aaa").start();

        new Thread(() -> {
            SynTest.read(Thread.currentThread().getName());
        }, "bbb").start();

        new Thread(() -> {
            SynTest.read(Thread.currentThread().getName());
        }, "ccc").start();
        //下面是调用write方法
        new Thread(() -> {
            SynTest.write(Thread.currentThread().getName());
        }, "AAA").start();

        new Thread(() -> {
            SynTest.write(Thread.currentThread().getName());
        }, "BBB").start();

        new Thread(() -> {
            SynTest.write(Thread.currentThread().getName());
        }, "CCC").start();
    }
}        

执行结果:即使调用不同方法,也实现了方法间的互斥。

【bbb】我是read方法!开始执行....
【bbb】我是read方法!执行结束!
【CCC】我是write方法!开始执行....
【CCC】我是write方法!执行结束!
【BBB】我是write方法!开始执行....
【BBB】我是write方法!执行结束!
【AAA】我是write方法!开始执行....
【AAA】我是write方法!执行结束!
【ccc】我是read方法!开始执行....
【ccc】我是read方法!执行结束!
【aaa】我是read方法!开始执行....
【aaa】我是read方法!执行结束!

二、对象锁

在方法内使用实例对象加锁

public class SynTest {

    public void toDo (String name) {
        synchronized (this) { //用的是对象锁
            System.out.printf("【%s】我是toDo方法!开始执行....%n", name);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.printf("【%s】我是toDo方法!执行结束!%n", name);
        }
    }
}

public class ConvertDemoApplication {

    public static void main(String[] args) throws InterruptedException {
    	//第一种,不同线程传入同一个对象
        SynTest test1 = new SynTest();
        new Thread(() -> {
            test1.toDo(Thread.currentThread().getName());
        }, "111").start();
        new Thread(() -> {
            test1.toDo(Thread.currentThread().getName());
        }, "222").start();
        new Thread(() -> {
            test1.toDo(Thread.currentThread().getName());
        }, "333").start();

        Thread.sleep(5000);
        //第二种 不同线程传入不同的对象
        new Thread(() -> {
            SynTest test = new SynTest();
            test.toDo(Thread.currentThread().getName());
        }, "444").start();
        new Thread(() -> {
            SynTest test = new SynTest();
            test.toDo(Thread.currentThread().getName());
        }, "555").start();
        new Thread(() -> {
            SynTest test = new SynTest();
            test.toDo(Thread.currentThread().getName());
        }, "666").start();
        new Thread(() -> {
            SynTest test = new SynTest();
            test.toDo(Thread.currentThread().getName());
        }, "RRR").start();
     }
}     

执行结果: 结论是对象锁使用相同对象能实现互斥,使用不同对象不能实现互斥。

【111】我是toDo方法!开始执行....
【111】我是toDo方法!执行结束!
【333】我是toDo方法!开始执行....
【333】我是toDo方法!执行结束!
【222】我是toDo方法!开始执行....
【222】我是toDo方法!执行结束!

【444】我是toDo方法!开始执行....
【555】我是toDo方法!开始执行....
【666】我是toDo方法!开始执行....
【444】我是toDo方法!执行结束!
【555】我是toDo方法!执行结束!
【666】我是toDo方法!执行结束!

四、方法锁

方法锁是将synchronized放在方法上,这个和实例对象锁用法一样,放在方法内是锁住部分代码,放在方法上是锁住方法所有的代码;

五、结论

  1. 看是不是互斥锁,只需要看锁住的是不是同一个对象,类对象只有一个,所以无论多少个线程调用,都是同一个对象,所以一定是互斥的,但是用的是实例对象的话,就要看不同线程传入的是不是同一个对象,如果是,则是互斥锁,如果不是同一个对象,则不是互斥锁。
  2. 在实际的项目中,由于spring管理的bean都是单例的,比如controller都是单例的,所以使用实例对象锁时就是互斥的,如果controller不是单例的,那么多线程环境下就不是互斥锁。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值