本篇文章主要介绍Java提供的synchronized同步关键字的三种使用方式以及使用上的区别。本篇文章的主要目的是让大家对synchronized关键字有个基本认识(照顾初级同学,大佬可以直接略过),后续会发布一篇深度剖析synchronized内部实现原理以及JDK1.6优化后的锁升级过程(无锁->偏向锁->轻量级锁->重量级锁)的文章供大家学习参考。
synchronized简单介绍
synchronized是Java提供的一个同步处理的关键字,可以保证同一时刻只有一个线程能够进入该方法(代码块),同时也是Java中解决并发问题的一种最常用最简单的方法。JDK1.6之前synchronized采用重量级锁的方式进行同步处理(效率低),JDK1.6之后进行了优化根据不同情况选择不同的锁,存在一个锁升级过程(效率高)。注:synchronized在JDK1.6之后锁升级过程会在下一篇文章进行详细介绍。
最后,synchronized有三种使用方式:修饰实例方法、修饰静态方法、同步代码块。
synchronized修饰实例方法
修饰实例方法使用的是对象级锁,多个线程执行同一个对象中被 synchronized修饰的方法时同时有且只有一个线程能够获取对象锁执行方法,其他线程等待。
class ThreadTest implements Runnable{ public static int i = 0; /** * synchronized 修饰实例方法 */ public synchronized void increase(){ i++; } @Override public void run() { for(int j=0;j<1000000;j++){ increase(); } }}public class Main { public static void main(String []args) throws Exception{ ThreadTest instance=new ThreadTest(); Thread t1=new Thread(instance); Thread t2=new Thread(instance); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(ThreadTest.i); }}//运行结果为2000000
i++并不是原子操作,而是首先读取i,然后进行++,最后将算出的值赋值给i,所以当多个线程同时进行i++操作时结果是不预期的,我们这里通过synchronized修饰increase方法保证increase方法的原子性,从而保证某个线程在进行i++操作的时候,其他线程是无法进行i++操作的,即顺序执行。
synchronized修饰静态方法
修饰静态方法使用是当前类class对象锁,多个线程执行同一类中被 synchronized修饰的静态方法时有且只有一个线程能够获取当前类的class对象锁执行方法,其他线程等待。
class ThreadTest implements Runnable{ public static int i = 0; /** * synchronized 修饰实例方法 这里将方法修改为了static方法 */ public static synchronized void increase(){ i++; } @Override public void run() { for(int j=0;j<1000000;j++){ increase();//这里调用的方法是属于ThreadTest类的静态方法 } }}public class Main { public static void main(String []args) throws Exception{ ThreadTest instance=new ThreadTest(); Thread t1=new Thread(instance); Thread t2=new Thread(instance); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(ThreadTest.i); }}//结果还是为2000000
synchronized修饰同步代码块
修饰同步代码块使用的是代码块括号中的对象锁,多个线程执行同一个被 synchronized的同步代码块时有且只有一个线程能够获取代码块括号中的对象锁执行代码块内容,其他线程等待。
class ThreadTest implements Runnable{ public static int i = 0; public static int y = 0; public Object lock = new Object(); /** * synchronized 修饰实例方法 */ public void increase(){ y++;//未被同步修饰,线程不安全 synchronized (lock){//lock为代码块对象锁 i++; } } @Override public void run() { for(int j=0;j<1000000;j++){ increase(); } }}public class Main { public static void main(String []args) throws Exception{ ThreadTest instance=new ThreadTest(); Thread t1=new Thread(instance); Thread t2=new Thread(instance); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("x="+ThreadTest.i); System.out.println("y="+ThreadTest.y); }}/*结果:x=2000000y=1999465*/
由结果可知,被synchronized修饰的代码块保证了多线程通知访问同时只有一个线程能够进入代码块中,保证了i被多线程处理的安全,y则在多线程处理过程中出现了不可预期的结果。
synchronized总结归纳
synchronized虽然有3种使用方式,但是总的来说synchronized都是控制对象的lock和unlock操作。一个线程取得被synchronized修饰的方法(实例或者静态)/代码块的执行资源时,控制的对象进行被锁定,然后执行具体内容,结束时对象解锁,其他线程即可竞争从而执行相关方法/代码块。
注:使用synchronized关键字修饰的同步块尽可能小(在不影响正常业务流程的情况下)。
END
笔者是一位热爱互联网、热爱互联网技术、热于分享的年轻人,如果您跟我一样,我愿意成为您的朋友,分享每一个有价值的知识给您。喜欢作者的同学,点赞+转发+关注哦!
点赞+转发+关注,私信作者“读书笔记”即可获得BAT大厂面试资料、高级架构师VIP视频课程等高质量技术资料。
本文来自投稿,不代表本人立场,如若转载,请注明出处:http://www.souzhinan.com/kj/250899.html