目录
synchronized和lock(ReentrantLock)锁的区
在 Java 中,关键字 synchronized可以保证在同一个时刻,只有一个线程可以执行某个方法或者某个代码块(主要是对方法或者代码块中存在共享数据的操作),同时我们还应该注意到synchronized另外一个重要的作用,synchronized可保证一个线程的变化(主要是共享数据的变化)被其他线程所看到(保证可见性,完全可以替代Volatile功能),这点确实也是很重要的。
synchronized的三种应用方式:
- 修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁
- 修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁
- 修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。
1.修饰实例方法:
public class AccountingSync implements Runnable{
//共享资源(临界资源)
static int i=0;
/**
* synchronized 修饰实例方法
*/
public synchronized void increase(){
i++;
}
@Override
public void run() {
for(int j=0;j<1000000;j++){
increase();
}
}
public static void main(String[] args) throws InterruptedException {
AccountingSync instance=new AccountingSync();
Thread t1=new Thread(instance);
Thread t2=new Thread(instance);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
/**
* 输出结果:
* 2000000
*/
}
以上Synchronized修饰increase方法,t1,t2都是对象instance的线程。
1.同一对象的不同线程不能同时访问synchronized修饰的实例方法(两个方法也不行),因为是同一把锁,一个对象只有一把锁。
2.同一对象的不同线程可以同时访问synchronized修饰的实例方法和另一非sychrinized修饰的方法。
3.不同对象的线程可以同时访问sychronized修饰的方法。
但是第三条这种情况如果两个对象的线程操作的是共享数据,还会有线程安全问题存在,解决方法是将increase声明为静态方法,用sychronized修饰给类对象加锁。
即第二种
2.修饰静态方法
public class AccountingSyncClass implements Runnable{
static int i=0;
/**
* 作用于静态方法,锁是当前class对象,也就是
* AccountingSyncClass类对应的class对象
*/
public static synchronized void increase(){
i++;
}
/**
* 非静态,访问时锁不一样不会发生互斥
*/
public synchronized void increase4Obj(){
i++;
}
@Override
public void run() {
for(int j=0;j<1000000;j++){
increase();
}
}
public static void main(String[] args) throws InterruptedException {
//new新实例
Thread t1=new Thread(new AccountingSyncClass());
//new新实例
Thread t2=new Thread(new AccountingSyncClass());
//启动线程
t1.start();t2.start();
t1.join();t2.join();
System.out.println(i);
}
}
由于synchronized关键字修饰的是静态increase方法,与修饰实例方法不同的是,其锁对象是当前类的class对象。
注意代码中的increase4Obj方法是实例方法,其对象锁是当前实例对象,与increase的锁对象不同,所以不是同一把锁,不会互斥。
但我们应该意识到这种情况下可能会发现线程安全问题(操作了共享静态变量i)
3.修饰代码块
也叫Sychronized阻挡,即不将整个方法互斥,只修饰方法中的部分代码块。
public void increase(){
synchronized (this) {
i++;
}
}
与修饰实例方法效果相同,this指当前对象,进一步看出锁对象是当前实例对象。
public static void increase(){
synchronized (AccountingSync.class) {
i++;
}
}
与修饰静态方法效果相同,AccountingSync.class指当前类对象,进一步看出锁对象是当前类对象。
Lock 锁:
Lock接口实现类
entrance:进入
public class ReentrantReadWriteLock implements ReadWriteLock
ReentrantLock构造器
true是公平锁,false是非公平锁
为什么默认是非公平锁,如果有两个线程一个3s一个3h,3s在3h后面,那样3s的虽然执行时间很短也要等3h才能执行。
Lock使用:
// 1、创建锁
private Lock lock = new ReentrantLock();
// 2、加锁
lock.lock();
try {
// 3、access the resource protected by the lock
// 业务代码
} finally {
// 4、解锁
lock.unlock();
}
synchronized和lock(ReentrantLock)锁的区别
1、synchronized内置的java关键字,Lock是一个java类
2、synchronized无法判断获取锁的状态, Lock可以判断是否获取到了锁 boolean isLocked()
3、synchronized会自动释放锁,Lock必须要手动释放锁!如果不是释放锁,会产生死锁
4、synchronized 线程1(获得锁),线程2(等待); Lock锁就不一定会等待下去,lock.tryLock()可以尝试去获取锁,不会一直等待,等不到就结束。
5、synchronized 和Lock锁都是 可重入锁,悲观锁,synchronized 非公平的锁,Lock默认是非公平锁,可以设置为公平锁, synchronized不可以中断(interrupt和stop都不可中断)。
6、synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码
部分转载自
深入理解Java并发之synchronized实现原理_zejian_的博客-CSDN博客_java synchronized原理