浅析Java 并发编程中的synchronized
synchronized关键字,我们一般称之为“同步锁”,用它来修饰需要同步的方法和需要同步代码块,默认是当前对象作为锁的对象。在用synchronized修饰类时(或者修饰静态方法),默认是当前类的Class对象作为锁的对象,故存在着方法锁、对象锁、类锁这样的概念。
一、没有设置线程同步的情况
先给出以下代码感受下代码执行的时候为什么需要同步?代码可能比较枯燥,配上业务理解起来就会舒服很多,学生军训,有三列,每列5人,需要报数,每个线程负责每一列报数。
classSynchronizedExample{ protectedstaticintnum=0; protectedvoidnumberOff(){ for(inti=0;i<5;i++){ num++; System.out.println(Thread.currentThread().getName()+":"+SynchronizedExample.num); } } } publicclassSynchronizedTest{ publicstaticvoidmain(String[]args)throwsInterruptedException{ SynchronizedExamplese=newSynchronizedExample(); for(inti=1;i<=3;i++){ newThread(()->{se.numberOff();},"线程"+i).start(); } } }
执行结果如下:
线程1:1
线程2:2
线程1:3
线程3:4
.......
之所以出现这种情况,是因为三个线程是异步的,没有同步。
对应的业务场景就是,在第一列没有完成报数的时候,其他队列抢报了,这在现实中是不允许的,所以需要类似于synchronized等具有同步功能的关键字粉末登场。
二、方法同步锁
当报数方法加上synchronized关键字之后,就会一列一列的报数。
protectedsynchronizedvoidnumberOff(){ for(inti=0;i<5;i++){ num++; System.out.println(Thread.currentThread().getName()+":"+SynchronizedExample.num); } }
执行结果如下:
线程1:1
线程1:2
线程1:3
线程1:4
......
写到这里还是要从技术层面讲下原理,当一个线程执行带有synchronized关键字的方法时,该线程会在该方法处设置一个锁(其他线程打不开这个锁,只能在外边等该线程释放掉该锁,一般都是执行完所有代码后主动释放锁),表示此方法是当前线程独占的,对应到上述业务中就是一次只能有一个队列报数。
三、对象锁
改进后的代码用到了一个对象锁,该对象锁默认是当前对象,上述代码等同于以下代码:
protectedvoidnumberOff(){ synchronized(this){ for(inti=0;i<5;i++){ num++; System.out.println(Thread.currentThread().getName()+":"+SynchronizedExample.num); } } }
当多个线程用一个对象锁,各个线程可以达到同步的作用,如果每个线程都用自己的对象锁,那么synchronized就失去了同步的作用。如以下代码:
classSynchronizedExample{ protectedstaticintnum=0; protectedvoidnumberOff(){ synchronized(this){ for(inti=0;i<5;i++){ num++; System.out.println(Thread.currentThread().getName()+":"+SynchronizedExample.num); } } } } publicclassSynchronizedTest{ publicstaticvoidmain(String[]args)throwsInterruptedException{ SynchronizedExamplese=newSynchronizedExample(); for(inti=1;i<=3;i++){ newThread(()->{newSynchronizedExample().numberOff();},"队列"+i).start(); } } }
执行结果如下:
线程1:1
线程2:2
线程1:3
线程3:4
.......
有读者会说不同线程执行的是不同对象中的方法,肯定达不到同步的效果,也对,也很有道理,接着看如下代码:
classSynchronizedExample{ protectedstaticintnum=0; protectedvoidnumberOff(Objectlock){ synchronized(lock){ for(inti=0;i<5;i++){ num++; System.out.println(Thread.currentThread().getName()+":"+SynchronizedExample.num); } } } } publicclassSynchronizedTest{ publicstaticvoidmain(String[]args)throwsInterruptedException{ SynchronizedExamplese=newSynchronizedExample(); for(inti=1;i<=3;i++){ newThread(()->{se.numberOff(newObject());},"队列"+i).start(); } } }
执行结果如下:
线程1:1
线程2:2
线程1:3
线程3:4
.......
四、类锁
对于上述问题,读者应该得出一个结论,要想达到同步的效果,必须用同一个锁,此时类锁可以粉末登场。看如下代码:
protectedvoidnumberOff(Objectlock){ synchronized(SynchronizedExample.class){ for(inti=0;i<5;i++){ num++; System.out.println(Thread.currentThread().getName()+":"+SynchronizedExample.num); } } }
上述代码可以达到同步的效果。
五、静态锁
静态锁是针对静态方法而言,当一个静态方法中有synchronized关键字时,默认的是使用当前类字节码对象作为锁。代码示例如下:
classSynchronizedExample{ protectedstaticintnum=0; protectedsynchronizedstaticvoidnumberOff(){ for(inti=0;i<5;i++){ num++; System.out.println(Thread.currentThread().getName()+":"+SynchronizedExample.num); } } } publicclassSynchronizedTest{ publicstaticvoidmain(String[]args)throwsInterruptedException{ for(inti=1;i<=3;i++){ newThread(()->{newSynchronizedExample().numberOff();},"队列"+i).start(); } } }
六、线程池实现
最后用线程池将上述代码写一下
packageioo; importjava.util.concurrent.ExecutorService; importjava.util.concurrent.Executors; classSynchronizedExample{ protectedstaticintnum=0; protectedsynchronizedstaticvoidnumberOff(){ for(inti=0;i<5;i++){ num++; System.out.println(Thread.currentThread().getName()+":"+SynchronizedExample.num); } } } publicclassSynchronizedTest{ publicstaticvoidmain(String[]args)throwsInterruptedException{ ExecutorServiceexecutorService=Executors.newCachedThreadPool(); for(inti=1;i<=3;i++){ executorService.execute(()->newSynchronizedExample().numberOff()); } } }
以上就是浅析Java并发编程中的synchronized的详细内容,更多关于Java并发编程synchronized的资料请关注毛票票其它相关文章!