1):synchronized是Java的一个关键字,它能够将代码块(方法)锁起来
2):它是一个隐式锁,即:锁的持有与释放都是隐式的,我们无需干预
3):synchronized是非公平锁,它无法保证等待的线程获取锁的顺序
4):synchronized是一种互斥锁,一次只能允许一个线程进入被锁住的代码块
5):synchronized是一种内置锁/监视器锁,Java中每个对象都有一个内置锁(监视器,也可以理解成锁标记),而synchronized就是使用对象的内置锁(监视器)来将代码块(方法)锁定的
synchronized一般我们用来修饰三种东西:
修饰普通方法
修饰代码块
修饰静态方法
public class TestSy {
// 修饰普通方法,此时用的锁是TestSy对象(内置锁)
public synchronized void test() {
代码执行 ......
}
}
public class TestSy {
public void test() {
// 修饰代码块,此时用的锁是TestSy对象(内置锁)--->this
synchronized (this){
代码执行......
}
}
}
public class TestSy {
// 修饰静态方法代码块,静态方法属于类方法,它属于这个类,获取到的锁是属于类的锁(类的字节码文件对象)-->TestSy.class
public synchronized void test() {
代码执行.....
}
}
0):lock必须被显式地创建、锁定和释放,为了可以使用更多的功能,一般用ReentrantLock为其实例化。为了保证锁最终一定会被释放(可能会有异常发生),要把互斥区放在try语句块内,并在finally语句块中释放锁,尤其当有return语句时,return语句必须放在try字句中,以确保unlock()不会过早发生,从而将数据暴露给第二个任务。
1):与synchronized不同的是,它是显式锁,即锁的持有和释放都必须由我们手动编写
2):Lock对象锁还提供了synchronized所不具备的其他同步特性,如可中断锁的获取(synchronized在等待获取锁时是不可中的),超时中断锁的获取,等待唤醒机制的多条件变量Condition等
Lock lock = new ReentrantLock();//默认使用非公平锁,如果要使用公平锁,需要传入参数true
........
lock.lock();
try {
//更新对象的状态
//捕获异常,必要时恢复到原来的不变约束
//如果有return语句,放在这里
} finally {
lock.unlock(); //锁必须在finally块中释放
}
ReadWriteLock是读写锁接口,其实现类为ReetrantReadWriteLock。ReetrantLock实现了Lock接口。
lock:用来获取锁,如果锁被其他线程获取,处于等待状态。如果采用Lock,必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此一般来说,使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生。
lockInterruptibly:通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。
tryLock:tryLock方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。
tryLock(long,TimeUnit):与tryLock类似,只不过是有等待时间,在等待时间内获取到锁返回true,超时返回false。
unlock:释放锁,一定要在finally块中释放
ReentrantLock和synchronized关键字一样可以用来实现线程之间的同步互斥,但是在功能是比synchronized关键字更强大而且更灵活。
public interface ReadWriteLock {
Lock readLock(); //获取读锁
Lock writeLock(); //获取写锁
}
一个用来获取读锁,一个用来获取写锁。也就是说将文件的读写操作分开,分成2个锁来分配给线程,从而使得多个线程可以同时进行读操作。ReentrantReadWirteLock实现了ReadWirteLock接口,并未实现Lock接口。
不过要注意的是:
如果有一个线程已经占用了读锁,则此时其他线程如果要申请写锁,则申请写锁的线程会一直等待释放读锁。
如果有一个线程已经占用了写锁,则此时其他线程如果申请写锁或者读锁,则申请的线程会一直等待释放写锁。
ReetrantReadWriteLock同样支持公平性选择,支持重进入,锁降级。
public class testLock {
static Map<String, Object> map = new HashMap<String, Object>();
static ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
static Lock r = rwLock.readLock();
static Lock w = rwLock.writeLock();
//读
public static final Object get(String key){
r.lock();
try {
return map.get(key);
} finally {
r.unlock();
}
}
//写
public static final Object put(String key, Object value){
w.lock();
try {
return map.put(key, value);
} finally {
w.unlock();
}
}
}
只需在读操作时获取读锁,写操作时获取写锁。当写锁被获取时,后续的读写操作都会被阻塞,写锁释放后,所有操作继续执行。
网上的说法也是很多,小刘自己也浏览资料,梳理了一些。如下:
1.Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;
2.synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
3.Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;
4.通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
5.Lock可以提高多个线程进行读操作的效率。(可以通过readwritelock实现读写分离)
6.性能上来说,在资源竞争不激烈的情形下,Lock性能稍微比synchronized差点(编译程序通常会尽可能的进行优化synchronized)。但是当同步非常激烈的时候,synchronized的性能一下子能下降好几十倍。而ReentrantLock确还能维持常态。
7.synchronized不如lock灵活。Lock和synchronize都是默认使用非公平锁的。如果不是必要的情况下,不要使用公平锁
8.总的来说,一般只要不是需要lock的灵活性,还是推荐使用synchronized,毕竟synchronized 在JDK1.6后,被优化加强了很多。
参考资料:
https://juejin.im/post/6844903542440869896#heading-5