synchronized学习分享
一、简单介绍
- synchronized是java中的关键字,是一种同步锁
修饰一个代码块:被修饰的代码块被称为同步语句块,其作用的范围是大括号括起来的部分,作用的对象时调用这个方法的对象
修饰一个方法:被修饰的方法被称为同步方法,其作用的范围是整个方法,作用对象是调用这个方法的对象;
修饰一个静态方法:其作用的范围是整个静态方法,作用对象是这个类的所有对象;
修饰一个类:其作用的范围是synchronized后大括号括起来的部分,作用的对象是这个类的所有对象。
二、详细比较(含代码)
修饰一个代码块:
一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞。
不加同步synchronized
package net.work.util; public class CommTest { public static void main(String[] args) { SynThread synThread = new SynThread(); Thread synThread1 = new Thread(synThread, "SynThread1"); Thread synThread2 = new Thread(synThread, "SynThread2"); synThread1.start(); synThread2.start(); } } class SynThread implements Runnable { private static int count; @Override public void run() { // synchronized (this) { for (int i = 0; i < 5; i++) { System.out.println("线程名:" + Thread.currentThread().getName() + ":" + (count + i)); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } // } } }
结果:
加同步synchronized
package net.work.util; public class CommTest { public static void main(String[] args) { SynThread synThread = new SynThread(); Thread synThread1 = new Thread(synThread, "SynThread1"); Thread synThread2 = new Thread(synThread, "SynThread2"); synThread1.start(); synThread2.start(); } } class SynThread implements Runnable { private static int count; @Override public void run() { synchronized (this) { for (int i = 0; i < 5; i++) { System.out.println("线程名:" + Thread.currentThread().getName() + ":" + (count + i)); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
结果:
加同步synchronized,但创建两个SynThread对象
public static void main(String[] args) { SynThread synThread11 = new SynThread(); SynThread synThread22 = new SynThread(); Thread synThread1 = new Thread(synThread11, "SynThread1"); Thread synThread2 = new Thread(synThread22, "SynThread2"); synThread1.start(); synThread2.start(); }
结果:
原因: 这时创建了两个SyncThread的对象syncThread1和syncThread2,线程thread1执行的是syncThread1对象中的synchronized代码(run),而线程thread2执行的是syncThread2对象中的synchronized代码(run);我们知道synchronized锁定的是对象,这时会有两把锁分别锁定syncThread1对象和syncThread2对象,而这两把锁是互不干扰的,不形成互斥,所以两个线程可以同时执行。
修饰一个方法:
Synchronized修饰一个方法很简单,就是在方法的前面加synchronized,public synchronized void method(){}; synchronized修饰方法和修饰一个代码块类似,只是作用范围不一样,修饰代码块是大括号括起来的范围,而修饰方法范围是整个函数。如将上面的的run方法改成如下的方式,实现的效果一样。
public synchronized void run() { { for (int i = 0; i < 5; i++) { try { System.out.println("线程名:"+Thread.currentThread().getName() + ":" + (count++)); Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
Synchronized作用于整个方法的写法。
写法一 public synchronized void method() { // todo } 写法二 public void method() { synchronized(this) { } } //写法一修饰的是一个方法,写法二修饰的是一个代码块,但写法一与写法二是等价的,都是锁定了整个方法时的内容
在定义接口方法时不能使用synchronized关键字。 构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步
修饰一个静态的方法
我们知道静态方法是属于类的而不属于对象的。同样的,synchronized修饰的静态方法锁定的是这个类的所有对象。
public synchronized static void method() { for (int i = 0; i < 5; i ++) { try { System.out.println(Thread.currentThread().getName() + ":" + (count++)); Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } public synchronized void run() { method(); }
修饰一个类
class ClassName { public void method() { synchronized(ClassName.class) { } } }
效果和上面synchronized修饰静态方法是一样的,synchronized作用于一个类T时,是给这个类T加锁,T的所有对象用的是同一把锁
三、对比lock
区别如下:
来源:
lock是一个接口,而synchronized是java的一个关键字,synchronized是内置的语言实现;
异常是否释放锁:
synchronized在发生异常时候会自动释放占有的锁,因此不会出现死锁;而lock发生异常时候,不会主动释放占有的锁,必须手动unlock来释放锁,可能引起死锁的发生。(所以最好将同步代码块用try catch包起来,finally中写入unlock,避免死锁的发生。)
是否响应中断
lock等待锁过程中可以用interrupt来中断等待,而synchronized只能等待锁的释放,不能响应中断;
是否知道获取锁
Lock可以通过trylock来知道有没有获取锁,而synchronized不能;
Lock可以提高多个线程进行读操作的效率。(可以通过readwritelock实现读写分离) 在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于 synchronized。所以说,在具体使用时要根据适当情况选择。
synchronized使用Object对象本身的wait 、notify、notifyAll调度机制,而Lock可以使用Condition进行线程之间的调度,
1、synchronized和lock的用法区别
synchronized:在需要同步的对象中加入此控制,synchronized可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象。
lock:一般使用ReentrantLock类做为锁。在加锁和解锁处需要通过lock()和unlock()显示指出。所以一般会在finally块中写unlock()以防死锁。
2、synchronized和lock性能区别
synchronized是托管给JVM执行的,
而lock是java写的控制锁的代码。
在Java1.5中,synchronize是性能低效的。因为这是一个重量级操作,需要调用操作接口,导致有可能加锁消耗的系统时间比加锁以外的操作还多。相比之下使用Java提供的Lock对象,性能更高一些。
但是到了Java1.6,发生了变化。synchronize在语义上很清晰,可以进行很多优化,有适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等等。导致在Java1.6上synchronize的性能并不比Lock差。官方也表示,他们也更支持synchronize,在未来的版本中还有优化余地。
2种机制的具体区别:
synchronized原始采用的是CPU悲观锁机制,即线程获得的是独占锁。独占锁意味着其他线程只能依靠阻塞来等待线程释放锁。而在CPU转换线程阻塞时会引起线程上下文切换,当有很多线程竞争锁的时候,会引起CPU频繁的上下文切换导致效率很低。
而Lock用的是乐观锁方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。乐观锁实现的机制就是CAS操作(Compare and Swap)。我们可以进一步研究ReentrantLock的源代码,会发现其中比较重要的获得锁的一个方法是compareAndSetState。这里其实就是调用的CPU提供的特殊指令。
现代的CPU提供了指令,可以自动更新共享数据,而且能够检测到其他线程的干扰,而 compareAndSet() 就用这些代替了锁定。这个算法称作非阻塞算法,意思是一个线程的失败或者挂起不应该影响其他线程的失败或挂起的算法。
3、synchronized和lock用途区别
synchronized原语和ReentrantLock在一般情况下没有什么区别,但是在非常复杂的同步应用中,请考虑使用ReentrantLock,特别是遇到下面2种需求的时候。
1.某个线程在等待一个锁的控制权的这段时间需要中断
2.需要分开处理一些wait-notify,ReentrantLock里面的Condition应用,能够控制notify哪个线程
3.具有公平锁功能,每个到来的线程都将排队等候
四、总结
1、 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是类,该类所有的对象同一把锁。
2、每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
3、实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制