当使用synchronized关键字时,实际上是获得了当前对象上的锁。在Java中,每一个对象都有与之关联的锁。这个关键字可以用到任意的代码块中。例如,将一段代码包含到synchronized块中,就可以在这段代码中提供原子操作,如下所示:
synchronized (this){
//program statements;
}
同步语句获得了由this关键字制定的当前对象上的锁,并且为原子操作来执行包含在花括号中的所有程序语句。this仅仅是指向当前对象的引用,此外,还可以使用指向其他对象的引用,程序将获得制定对象上的锁。(在获得线程执行块的整个期间,将会锁定整个对象。因此,为了调用这个对象的其他方法,其他任何的线程都不得不等待)
调用synchronized方法的顺序不当,可能造成死锁,不恰当或者过度的同步还会导致应用程序的效率的降低
比喻:
有两个运行在不同处理器上的线程,两者都可以访问某个通用变量,很明显,这个变量存在于机器的通用主存中,两个处理器都可能缓存这个变量。因此,在没有同步的情况下,从处理器各自的缓存中读取,对于同一个通用变量,这两个线程可能看见不同的值。当对监视器同步时,在获得锁之后,Java内存模型(Java Memory Model,JMM)要求缓存立即消失,然后在释放之前刷新缓存,频繁的刷新缓存可能很耗费资源,这就解释了使用同步时的性能损失问题。
即使当程序中只包含运行在单个处理器的单个线程上时候,同步方法的调用也要比非同步方法的调用耗费更多的时间。如果同步需要竞争锁,那么很大程度上提高性能的损失。这是因为在获得锁之前,系统中可能发生若干个线程切换和系统调用。
最佳选择方法:多线程程序需要足够的同步以便保护共享数据,避免他们受到破坏,但不要过分同步,进而避免死锁或者性能上的下降,需要在两者之间平衡,使用volatile关键字的结果是以更高效的方法实现同步。
private int sharedVariable;
public synchronized int getSharedVariable(){
return sharedVariable;
}
public synchronized void setSharedVariable(int sharedVariable){
this.sharedVariable=sharedVariable;
}
下面的代码给出更好的方式:
private volatile int sharedVariable;
public int getSharedVariable(){
return sharedVariable;
}
public void setSharedVariable(int sharedVariable){
this.sharedVariable=sharedVariable;
}
现在,我们将变量标记为volatile,而不是将访问方法设置为synchronized,这样做更高效,
因为volatile只是在线程和内存之间同步变量的值,而同步方法除了要锁定和释放监视器外,还要为线程的所有变量在线程和主存之间提供同步。
死锁:当两个或者多个线程竞争以获得某个共享资源的锁时,没有线程能够继续执行,除非其他的线程将自己的持有的锁释放。死锁发生后,所有的竞争线程都不能继续。
死锁的解决办法;
1.锁的顺序
2. 锁超时,
3. 死锁检测
1锁的顺序:如果所有的线程都以同样的顺序来或者和释放多个对象的锁,那么死锁就不会发生。一般规则是:存在多个锁的情况下,如果任意线程总是按照相同顺序来获得所有的锁,死锁就不可能发生,但是,并不是总是能够在获得任何锁之前都知道所需要的锁。
2锁超时:在给定的超时期限内,如果线程没有成功的获得所必须的锁,线程将回退,释放目前所有获得的锁,然后在等待随机 的时间内尝试再次获得锁。这种随机的等待时间给了其他的线程获得锁的公平机会。
Lock lock=new ReentrantLock();
lock.lock();
lock.unlock();
可以使用tryLock(long timeout,TimeUnit timeUnit)方法来指定试图获取某个锁的超时时间,包含在lock(),unlock()之间的语句代码会作为原子操作执行,并且从不损坏系统的完整性
3 死锁检测:首先记录所有的线程的每一个请求和获得每一个锁,通常情况下,为了方便遍历,他们被存储在映射或者视图中,当对锁的请求被拒绝后,线程会遍历这个锁图来检查死锁。
解决办法:1 释放所有的锁并且撤回所有等待的请求,然后在每一个线程试图获取所期望的锁之前等待某个随机访问。这无法保证重新获得锁的第二次尝试会成功,并且我们可能需要重复整个过程若干次,尤其当被调用的线程的数目非常大的时候。
2 进行基于优先级的回退,只是回退几个被分配了较低优先级的线程,而其他的线程继续持有他们的锁。当检测到死锁时,这些回退的线程自身的优先级可能是随机分配的。