synchronized关键字

一、synchronized关键字(Java线程同步中的一个重要的概念,是一种同步锁)    参考链接:http://www.importnew.com/21866.html中修饰一个代码块所带的实例完美的解释了synchronized修饰一个代码块的基本原理。

    1、可以修饰一个代码块,对象锁。

      【在创建了一个实例对象后,假如该实例对象创建了两个线程,当这两个thread都start后,在调用该对象的synchronized代码块时,线程不可并行;而同时new两个实例对象,并且这两个实例对象分别创建了一个线程去调用synchronized代码块时,此时这两个线程是可以并行的,这源于synchronized关键字作用的对象时于调用该代码块的对象。】

      【一个类中既有synchronized代码块,又有非synchronized代码块时,同时启用两个线程分别调用synchronized代码块和非synchronized代码块时,是互不影响的。】

      【对一个类对象加了synchronized关键字,那么当线程启用后,谁先占用了该对象谁就会先执行完,知道释放了该对象锁位为止。】

 

    2、可以修饰一个类

      【synchronized修饰一个方法就是在定义方法前加上关键字,基本与修饰代码块相同,在方法前加关键字和在方法内对整个方法的代码段加关键字的意义相同】

      【synchornized不可以被继承,比如父类有一个方法被synchronized修饰,子类直接写该方法,并不是synchronized的,可以选择加上关键字,也可以选择调用父类的此方法。】

      【synchronized不可以修饰接口。】

      【构造方法不能被synchronized直接修饰,可以通过修饰代码块来实现】

 

    3、可以修饰一个静态方法

      【在修饰一个静态方法时,由于静态方法是属于类的,而不是属于对象的,所以整个类是被锁定的。对于分别创建两个对象调用该被synchronized修饰的静态方法时必须一个一个执行,因为这两个对象相当于用的是同一把锁】

 

    4、可以修饰一个类

      【在修饰一个类时加的是类锁,类的所有对象都是同一把锁】

 

    5、问题:

      【对于一个类中,如果一个静态方法被synchronized修饰了,而其他的没有被修饰,那么在创建了两个对象时,分别访问不同的类时,是否是并行的?】

 

 

    20190318更新

    6、一般会将synchronized和lock对比。

        【关于synchronized字段,不管该关键字是修饰方法还是修饰同步代码块,synchronzed拿到的都是对象。当synchronized修饰的是方法时,synchronized所拿到的是调用该方法的对象的锁,一般情况下都是this的锁;当synchronized()修饰的同步代码块时,synchronized拿到的是指定对象的锁。java中的对象每个只含有一个锁,通过synchronized来获取对象的锁。对象锁:又称独占排它锁,通过名字我们便可知道,在多线程程序中,一旦一个线程到达了共享区(即synchronized修饰的区域)。那么该线程将拥有该共享区的对象锁,其他线程想要进入,只能等到该线程释放了锁才可进入。对应于非静态方法和非静态代码块。】

        【当synchronized修饰的静态方法时,由于静态方法不包含this,属于类层次的方法,所以,synchronized拿到的是这个方法所属Class对象的锁。类锁:方法或代码块所在类的类对象的锁。对应于静态方法和静态代码块。】

        【当方法异常退出时,其对象锁可以由JVM进行释放。】

        【占有锁的线程释放锁时一般是以下三种情况:占有锁的线程执行完了代码块,然后释放对锁的占有;占有锁的线程发生了异常,此时JVM会让线程自动释放锁; 占有锁的线程调用了wait()方法,从而进入了WAITING状态需要释放锁。】

    7、synchronized关键字在hashtable中的get put remove方法中 都增加了该关键字,所以保证了hashtable的线程安全。另外:每个操作数据的方法都进行同步控制之后,由此带来的问题是任何一个时刻只能有一个线程可以操纵Hashtable,所以其效率比较低。所以synchronized的一个缺点就是:加锁后导致效率上的降低,比如hashtable。

二、Lock接口

    1、类图https://baike.baidu.com/item/%E7%B1%BB%E5%9B%BE/4670826?fr=aladdin介绍:

 

    2、lock接口实现了如下几个方法

        【Lock():是Lock接口中使用最多的获取锁的方法,如果锁被其他线程占用就等待。由于Lock接口是基于JDK层面的,所以,锁的释放动作必须手动进行。不像synchronized是基于Java语言的特性,属于JVM层面,锁的获取和释放动作都由JVM自动进行,对开发者是透明的。】

使用方式:

Lock lock = ...; lock.lock();

try{    

   //处理任务

}catch(Exception ex){
}finally{

    lock.unlock();   //释放锁

}

        【tryLock():表示用来尝试获取锁,如果获取成功即返回TRUE,如果锁被其他线程占用,则返回FALSE;该方法会立即返回结果,不会一直处于等待状态。】

        【tryLock(long time,TimeUnit unit):与tryLock类似,区别在于这个方法在拿不到锁的情况下会等待一个时间time,在时间期限之内如果还拿不到锁,就返回FALSE;同时可以相应中断。如果在一开始或者等待期间获得了锁,则返回true。】

问题:如果在开始没有拿到锁,那么代码块会进入到等待状态么?

猜想:我觉得会进入等待状态,不过在等待的这段时间内,会存在一个响应机制来监测别的线程是否释放了锁,如果释放了锁,则直接有该线程获取锁,该方法返回TRUE;如果没有释放,那么该方法将不再享有响应机制的提醒,并返回FALSE。

使用方式:

Lock lock = ...;

if(lock.tryLock()) {

     try{

         //处理任务     

   }catch(Exception ex){
     }finally{

         lock.unlock();   //释放锁     

  }

}else {   

  //如果不能获取锁,则直接做其他事情

}

        【lockInterruptibly:当通过这个方法尝试获取锁时,如果线程正在等待获取锁,那么这个线程能够响应中断,即被自己或者其他线程中断线程的等待状态。例如,当两个线程同时通过lock.lockInterruptibly()方法获取锁时,假如线程A获取了锁,那么线程B就只能进入等待状态,那么对线程B调用ThreadB.interrupt()能够中断线程B的等待状态。注意:lockInterruptibly方法必须放在try块中或者在调用lockInterruptibly的方法外声明抛出InterruptedException,推荐使用后者。】

使用方式:

public void method() throws InterruptedException {

    lock.lockInterruptibly();    

try { 

     //.....    

}     finally {  

       lock.unlock();    

}

疑问:释放掉线程的等待状态有什么用处?

相比于synchronized,等待状态的线程无法响应中断。提高了代码的灵活性,当不想持续的等待下去时,响应中断去做其余的事情,更具灵活性。

        【ReadWriteLock该锁提升了读操作的效率,不过要注意的是,如果有一个线程已经占用了读锁,则此时其他线程如果要申请写锁,则申请写锁的线程会一直等待释放读锁。如果有一个线程已经占用了写锁,则此时其他线程如果申请写锁或者读锁,则申请的线程也会一直等待释放写锁。】

 

三、Lock和synchronized的选择:

    1、 Lock是一个接口,属于JDK层面的实现;而synchronized属于Java语言的特性,其实现有JVM来控制。

    2、 synchronized在发生异常时,会自动释放掉锁,故不会发生死锁现(此时的死锁一般是代码逻辑引起的);而Lock必须在finally中主动unlock锁,否则就会出现死锁。

    3、 Lock能够响应中断,让等待状态的线程停止等待;而synchronized不行。

    4、 通过Lock可以知道线程是否成功获得了锁,而synchronized不行。

    5、 Lock提高了多线程下对读操作的效率。

 

四、锁的概念

    1、扩展可重入锁:synchronized和ReentrantLock都是可重入锁。当一个线程执行到某个synchronized方法时,比如说method1,而在method1中会调用另外一个synchronized方法method2,此时线程不必重新去申请锁,而是可以直接执行方法method2;

    2、可中断锁:通过上面的例子,我们可以得知Lock是可中断锁,而synchronized不是;

    3、公平锁:尽量以请求的顺序来获取锁,同是有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最先请求的线程)会获得该锁。synchronized是非公平锁,而ReentrantLock和ReentReadWriteLock默认情况下是非公平锁,但是可以设置成公平锁ReentrantLock lock = new ReentrantLock(true); ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);设置为TRUE即为公平锁,为FALSE或者无参数为非公平锁;

    4、读写锁:读写锁将对临界资源的访问分成了两个锁,一个读锁和一个写锁。即ReadWriteLock接口及其实现ReentrantReadWriteLock。

    5、乐观锁和悲观锁出现在并发的情况下操作数据库的时候,加锁是为了防止多个事务操作数据库时出现数据异常。

        【悲观锁:又称为写锁、独占锁。获取悲观锁后,既能读数据,又能修改数据。】

        【乐观锁:又称为读锁,获得乐观锁之后,可以查看但无法修改和删除数据。】

        【拿MySql的InnoDB引擎来说,对于insert、update、delete等操作。会自动给涉及的数据加排他锁;对于一般的 select语句,InnoDB不会加任何锁,事务可以通过以下语句给显示加共享锁或排他锁。乐观锁:SELECT ... LOCK IN SHARE MODE;悲观锁:SELECT ... FOR UPDATE;】

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值