*synchronize 锁的概念
java的内置锁:
每个java对象都可以用作一个实现同步的锁,这些锁称为内部锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时就会释放该锁。获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。
java内置锁是一个互斥锁,这就意味着最多只有一个线程获得该锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,直到线程B释放这个锁,如果B线程不释放该锁,那么线程A将永远等待下去。
java的对象锁和类锁:
java的对象锁和类锁在锁的概念上基本上和内置锁是一致的,但是,两个锁实际是有很大的区别的,
对象锁是用于对象实例方法或者一个对象实例上的;类锁是用于类的静态方法或者一个类的class对象上。我们知道,类的对象实例可以有很多个,但是每个类只有一个class对象,所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的。
synchronize的缺陷:
当某个线程进入同步方法获得对象锁,那么其他线程访问这里对象的同步方法时,必须等待或者阻塞,这对高并发的系统是致命的,这很容易导致系统的崩溃。如果某个线程在同步方里面发生了死循环,那么它就永远不会释放这个对象锁,那么其他线程就要永远的等待。这是一个致命的问题。
当然同步方法和同步代码都会有这样的缺陷,只要用了synchronize关键字就会有这样的风险和缺陷。既然避免不了这种错误,那么就应该将风险降到最低。这也是同步代码块在某种情况下要优于同步方法的方面。
例如在某个类的方法里面:这个类里面声明了一个对象实例,SynObject so=new SynObject();在某个方法里面调用了这个实例的方法so.testsy();但是调用这个方法需要进行同步,不能同时有多个线程同时执行调用这个方法。
这时如果直接用synchronized修饰调用了so.testsy();代码的方法,那么当某个线程进入了这个方法之后,这个对象其他同步方法都不能给其他线程访问了。假如这个方法需要执行的时间很长,那么其他线程会一直阻塞,影响到系统的性能。
如果这时用synchronized来修饰代码块:synchronized(so){so.testsy();},那么这个方法加锁的对象是so这个对象,跟执行这行代码的对象没有关系,当一个线程执行这个方法时,这对其他同步方法时没有影响的,因为他们持有的锁都完全不一样。
不过这里还有一种特例,就是上面演示的第一个例子,对象锁synchronized同时修饰方法和代码块,这时也可以体现到同步代码块的优越性,如果test1方法同步代码块后面有非常多没有同步的代码,而且有一个100000的循环,这导致test1方法会执行时间非常长,那么如果直接用synchronized修饰方法,那么在方法没执行完之前,其他线程是不可以访问test2方法的,但是如果用了同步代码块,那么当退出代码块时就已经释放了对象锁,当线程还在执行test1的那个100000的循环时,其他线程就已经可以访问test2方法了。这就让阻塞的机会或者线程更少。让系统的性能更优越。
一个类的对象锁和另一个类的对象锁是没有关联的,当一个线程获得A类的对象锁时,它同时也可以获得B类的对象锁。