学习笔记:synchronized

定义
synchronized是java中的一个关键字,是Java语言内置的特性。synchronized的使用主要有2种:同步方法和同步代码块。

使用synchronized需要明确的几个问题

  • 无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的对象访问。
  • 每个对象只有一个锁(lock)与之相关联。
  • 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。

synchronize方法

synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。synchronized修饰方法又分为修饰静态方法和修饰非静态方法。

synchronized修饰非静态方法
synchronized修饰非静态方法,是作用在同一对象上的。
当一个线程访问object的一个synchronized同步方法时,其他线程对该object的所有其它synchronized同步方法的的访问将被阻塞。

synchronized修饰静态方法
因为静态方法不属于类对象,它是属于类的,所以如果用synchronized修饰静态方法,那么它在所有类对象中都是同步的。
只要是这个类产生的对象,在已有访问情况下,再次调用这个静态方法时就会被阻塞。

synchronized代码块

我们在用synchronized关键字的时候,能缩小代码段的范围就尽量缩小,能在代码段上加同步就不要再整个方法上加同步。这叫减小锁的粒度,使代码更大程度的并发。因为当方法体过于庞大而需要同步的部分又很少,锁的时间就加长了,别的线程是不是要等很久。所以往往同步代码块比同步方法好用。synchronized代码块又分为这么几种:synchronized(this)synchronized(className.class)synchronized(Object obj)

synchronized(this)
synchronized(this)类似于前面的synchronized修饰非静态方法,锁都在当前对象,只限制当前对象对该代码块的同步。

synchronized(className.class)
synchronized(className.class)类似于前面的synchronized修饰静态方法,锁在类而不在类对象,只要是className类对象访问该代码块都被要求同步。

synchronized(Object obj)
这时锁就是对象,谁拿到这个锁谁就可以运行它所控制的那段代码。当有一个明确的对象作为锁时,就可以这样写程序,但当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的instance变量(它得是一个对象)来充当锁。
·这里的锁的作用范围取决于“lock”(方法内建立的对象)的作用域,谁能拿到这个lock就能访问该代码块。比如将lock作为staic全局变量就是类所有,这时synchronized (lock)就相当于synchronized (className.class);相反,将lock作为局部变量(放在方法内)该synchronized 将失效,因为每个访问该方法的都能获得一个lock对象。

Lock

synchronized 会自动释放锁,而Lock必须手动释放,如果没有释放就可能造成死锁。并且Lock的使用一般放在try{ }catch块中,最后在finally中释放锁,保证抛出异常时锁会被释放。Lock是一个接口。
lock()、tryLock()、tryLock(long time, TimeUnit unit)和lockInterruptibly()是用来获取锁的。unLock()方法是用来释放锁的。

  • lock()是使用的最多的,它就是用来获取锁,如果锁被其他线程拿到,它就等待。
  • tryLock()是有返回值的,尝试获取锁,成功就返回true失败就返回false。所以说这个方法无论拿不拿得到锁都会立即返回而不会在那等待。
  • tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。
  • lockInterruptibly()方法比较特殊,当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。也就使说,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。

volatile
volatile的使用场景,通过关键字sychronize可以防止多个线程进入同一段代码,在某些特定场景中,volatile相当于一个轻量级的sychronize,因为不会引起线程的上下文切换,一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:

  • 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。volatile关键字会强制将修改的值立即写入主存,使线程的工作内存中缓存变量行无效。
  • 禁止进行指令重排序。
    在java虚拟机的内存模型中,有主内存和工作内存的概念,每个线程对应一个工作内存,并共享主内存的数据。
  • 对于普通变量:读操作会优先读取工作内存的数据,如果工作内存中不存在,则从主内存中拷贝一份数据到工作内存中;写操作只会修改工作内存的副本数据,这种情况下,其它线程就无法读取变量的最新值。
  • 对于volatile变量,读操作时JMM会把工作内存中对应的值设为无效,要求线程从主内存中读取数据;写操作时JMM会把工作内存中对应的数据刷新到主内存中,这种情况下,其它线程就可以读取变量的最新值。

Lock与synchronized的不同

  • Lock支持在等待一定的时间或者能够响应中断。
  • Lock支持在多个线程都只是进行读操作时,线程之间不会发生冲突,通过Lock就可以办到。
  • 通过Lock可以知道线程有没有成功获取到锁。
  • Lock不是Java语言内置的。synchronized是Java语言的关键字,因此是内置特性。
  • Lock是一个类,通过这个类可以实现同步访问。
  • Lock必须要用户去手动释放锁,而synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值