您好,登录后才能下订单哦!
Java中synchronized关键字的作用分析,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。
一、简介:
synchronized关键字是用来控制线程同步的,就是在多线程的环境下,控制synchronized代码段不被多个线程同时执行。synchronized既可以加在一段代码上,也可以加在方法上。
1、举个栗子:
2、类文件编译
看看该class文件的编译后的文件,查看当前类的包,变量信息,堆栈信息等等,有助于理解synchronized到底是什么指令操作的:
命令:javap -verbose MySynchronized > mySynchronized.txt
可以看到jvm加锁的指令,monitorenter加锁,monitorexit释放锁的指令,至于怎么计数的,有兴趣的可以深入研究一下。
二、synchronized的方式概念:
类锁,对象锁,代码块锁,同步方法锁。
类锁:顾名思义是该类的锁,是初始化静态方法上的锁,与对象锁不一样,简单可以理解,类加载完就存在的锁。
对象锁:是new出来的实例,每个实例相互隔离。
方法锁:就是方法上加一个synchronized关键字,又区分为静态方法锁和实例方法锁,两者不一样。
代码块锁:就是锁一段代码,主要区别在括号里的对象synchronized(this){...},或者其他synchronized(lock){...}。
三、锁的几种用法和多线程下的阻塞分析
1、代码块锁,新建一个对象:Object lock = new Object(),这里锁的是这个对象的下的Object 对象,大部分人喜欢这样用,this是锁整个对象,范围比较大,可能造成该对象中其他加锁方法被干扰,所以可以用这种方式去防止大类对象被使用的时候造成死锁。
private Object lock = new Object();
/**
* 锁对象lock
*/
public void methodA(){
synchronized(lock){
System.out.println("methodA====start");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("methodA====end");
}
}
2、代码块锁,锁类对象锁本身,不同的对象相互隔离,互不干扰。
/**
* 锁对象本身
*/
public void methodB(){
synchronized(this){
System.out.println("methodB==start");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("methodB==end");
}
}
3、实例方法锁:
/**
* 实例方法锁,同一个实例,多线程阻塞
* 不同的实例,互不干扰,因为锁的不是一个对象
* 不同的实例,不同实例方法锁,多线程也是阻塞等待的,因为是一个对象
*/
public synchronized void methodD(){
System.out.println("methodD==start");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("methodD==end");
}
4、静态方法锁:
/**
* static方法,类级别的锁,更对象this无关,
* 同一个类中静态方法锁,多线程阻塞,等待获取类锁才能执行
* 同一个类中不同静态方法锁,多线程阻塞,也需要等待获取类锁才能执行
*/
public synchronized static void methodC(){
System.out.println("methodC==start");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("methodC==end");
}
5、最后说说wait和sleep和对象锁:
/**
* wait 会放弃对象锁,不会放弃CPU资源
* sleep 不会放弃对象锁,会放弃CPU资源
* 测试发现同一个对象,sleep的方法阻塞,wait方法会放弃对象 * 锁,继续多线程执行
*/
public synchronized void methodE(){
System.out.println("methodE==start");
try {
this.wait(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("methodE==end");
}
四、最后总结:
主要看synchronized 锁的是不是同一个对象,是一个对象,则下一个线程则需要等待上一个线程释放对象锁,(记住是对象锁)
五、什么场景用?
synchronized 虽然能保证线程安全,但是在并发场景下,会影响性能,比如在抢购场景下。还有加锁的方法或者加锁代码块是不是一个很耗时的流程,或者一个公共方法,很多业务逻辑都用到,这时候,并发环境下,基本行不通,不同的业务场景下不应该有干扰。所以尽量根据不同的场景和业务逻辑谨慎使用,也可以通过其他方式保证线程的安全,比如用并发包中的一些数据结构,或者ThreadLocal实现线程上下文变量一致性,防止其他程序对公共变量进行篡改。
六、扩展知识
1、很多人问,synchronized 和 实现Lock接口的对象都有加锁的功能,在开发的时候如何选择?
《深入理解Java虚拟机》线程安全和锁优化一章有简单做介绍,synchronized 和ReentrantLock在JDK1.5 中单核,ReentrantLock比synchronized 的性能要好,多核CPU 下synchronized还是比ReentrantLock好,这样可以看出synchronized 的性能并不差,所以在JDK1.6,JVM在synchronized 的优化,性能已经由于Lock,在一般的synchronized 能满足需求的情况下会使用synchronized 。
2、那Lock又有哪些优势呢?
synchronized 是java 的原生锁,而ReentrantLock是基于API的方式进行加锁的,和一些锁的其他操作。功能比synchronized 要强大,那就意味着用起来就灵活,可以实现很多synchronized 没有的功能,例如:
1.等待过程中,可以直接中断等待(如果一直没有获得锁的话)
2.公平锁:基于时间先后进行锁获取,不是synchronized 一样无序竞争。
3.一个ReentrantLock对象可以绑定多个锁对象,synchronized (Object)需要多个对象,而ReentrantLock只需多次调用newCondition()方法即可。
看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注亿速云行业资讯频道,感谢您对亿速云的支持。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。