当前位置: 首页 > 科技观察

面试突击:Synchronized有多少用途?

时间:2023-03-19 22:01:01 科技观察

在Java语言中,锁是保证线程安全的主要手段,Java中的锁主要有两种:synchronized和Lock。今天我们将重点介绍synchronized的几种用法。使用介绍使用synchronized不需要手动执行加锁和释放锁的操作,我们只需要声明synchronized关键字,JVM层面会自动帮我们执行加锁和释放锁的操作。synchronized可以用来装饰普通方法、静态方法和代码块,我们分别来看。1.修饰普通方法synchronized修饰普通方法的用法如下:/***synchronized修饰普通方法*/publicsynchronizedvoidmethod(){//....}called对于一个同步方法,它的作用范围是整个方法,作用对象是调用这个方法的对象。2.修饰静态方法synchronized修饰静态方法与修饰普通方法类似,其用法如下:/***synchronized修饰静态方法*/publicstaticsynchronizedvoidstaticMethod(){//......}whensynchronized在修饰静态方法时,它的作用范围是整个程序,这个锁对于所有调用这个锁的对象都是互斥的。所谓互斥就是同一时间只能有一个线程使用,其他线程只能排队等待。修饰普通方法VS修饰静态方法synchronized修饰普通方法和静态方法看似一样,其实完全不同。对于静态方法,synchronized锁定是全局的,即在整个程序运行过程中,所有调用这个静态方法的对象都是互斥的,而普通方法是针对对象级别的。不同的对象对应不同的锁。比如下面的代码调用了两次方法,但是获取锁的方式完全不同。实现代码如下:String[]args)throwsInterruptedException{//创建线程池同时执行任务ExecutorServicethreadPool=Executors.newFixedThreadPool(10);//执行两次静态方法threadPool.execute(()->{staticMethod();});threadPool.execute(()->{staticMethod();});//执行两次普通方法threadPool.execute(()->{SynchronizedUsageusage=newSynchronizedUsage();usage.method();});threadPool.execute(()->{SynchronizedUsageusage2=newSynchronizedUsage();usage2.method();});}/***synchronized修饰的普通方法*该方法执行耗时3s(因为有3s的休眠时间)*/publicsynchronizedvoidmethod(){System.out.println("GeneralMethod执行时间:"+LocalDateTime.now());try{//睡眠3sTimeUnit.SECONDS.sleep(3);}catch(InterruptedExceptione){e.printStackTrace();}}/***synchronized修饰的staticMethod*该方法的执行耗时3s(因为有3s的休眠时间)*/publicstaticsynchronizedvoidstaticMethod(){System.out.println("静态方法执行时间:"+LocalDateTime.now());try{//睡眠3sTimeUnit.SECONDS.sleep(3);}catch(InterruptedExceptione){e.printStackTrace();}}}上面程序的执行结果如下:从上面的结果可以看出,静态方法锁是全局的,它是针对所有调用者的;普通方法加锁是对象级别的,不同的对象有不同的锁3、修改代码块在我们日常开发中,最常见的就是给代码块加锁,而不是给方法加锁,因为给方法加锁就相当于给整个方法加锁,所以加锁的粒度太大的话太大,程序的执行性能会受到影响,所以一般情况下,我们会使用synchronized来锁定代码块。其实现语法如下:publicvoidclassMethod()throwsInterruptedException{//pre-code...//锁定代码synchronized(SynchronizedUsage.class){//......}//postcode...}从上面的代码我们可以看出,相比装饰方法,装饰代码块需要手动指定Locked对象,Locked对象通常用this或者xxx.class的形式来表示,比如下面的代码://lockaclasssynchronized(SynchronizedUsage.class){//......}//锁定当前类对象synchronized(this){//......}这个VS类使用synchronized来锁定thisandxxx.class就完全不同了,这个加锁的时候,就是跟当前对象加锁,每个对象对应一把锁;当使用xxx.class加锁时,表示使用某个类(不是类实例)加锁,是应用层的,全局有效的,如下代码所示:importjava.time.LocalDateTime;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;importjava.util.concurrent.TimeUnit;publicclassSynchronizedUsageBlock{publicstaticvoidmain(String[]args)throwsInterruptedException{//创建线程池并执行任务同时执行ExecutorServicethreadPool=Executors.newFixedThreadPool(10);//执行synchronized(this)两次threadPool.execute(()->{SynchronizedUsageBlockusage=newSynchronizedUsageBlock();usage.thisMethod();});threadPool.execute(()->{SynchronizedUsageBlockusage2=newSynchronizedUsageBlock();usage2.thisMethod();});//执行两次synchronized(xxx.class)threadPool.execute(()->{SynchronizedUsageBlockusage3=newSynchronizedUsageBlock();usage3.classMethod();});threadPool.execute(()->{SynchronizedUsageBlockusage4=newSynchronizedUsageBlock();usage4.classMethod();});}/***synchronized(this)lock*这个方法的执行需要3s(因为有3s的休眠时间)*/publicvoidthisMethod(){synchronized(this){System.out.println("synchronized(这)锁:"+LocalDateTime.now());尝试{//睡眠3sTimeUnit.SECONDS.sleep(3);}catch(InterruptedExceptione){e.printStackTrace();}}}/***synchronized(xxx.class)lock*该方法的执行耗时3s(因为有3s的休眠时间)*/publicvoidclassMethod(){synchronized(SynchronizedUsageBlock.class){System.out.println("同步(xxx.class)锁:"+LocalDateTime.now());try{//睡眠3sTimeUnit.SECONDS.sleep(3);}catch(InterruptedExceptione){e.printStackTrace();}}}}上面程序的执行结果如下:总结一下synchronized的三种用法,可以用来修饰普通方法、Static方法和代码块,最常用的是修饰代码块,还有一个装饰代码块时需要指定锁对象。这个锁对象通常用this或者xxx.class来表示。使用this时表示使用当前对象进行加锁,使用class时表示使用某个类(非类对象实例)进行加锁,全局有效