synchronized
synchronized同步锁是使用synchronized关键字将一段代码逻辑锁起来,只有获得这把锁的线程才能访问,并且同一时刻只能有一个线程持有这把锁,保证了线程安全(悲观锁)
sychronized是一种可重入锁,能被同一个线程反复获取,在获取时不仅要哦按段是否是第一次获取还要怕判断是第几次获取,每次退出sychronized次数减一,直到次数为0才算完全退出。
synchronized关键字的用法
1.修饰实例方法:当前对象this充当锁,只有获取this锁的线程才能访问该方法
class Counter1 {
// 用于计数的公共变量
public static int count = 0;
// 递增
public void add() {
for (int i = 0; i < 10000; i++) {
synchronized (this) {
Counter1.count += 1;
}
}
}
// 递减
public synchronized void dec() {
for (int i = 0; i < 10000; i++) {
Counter1.count -= 1;
}
}
}
2.修饰静态方法:使用当前对象的Class对象充当锁,只有获取Class对象的线程才能访问该方法
public class Demo03 {
public static void main(String[] args) {
//创建foo实例对象
Foo f=new Foo();
//创建线程
Thread th=new Thread() {
@Override
public void run() {
f.dosth1();
}
};
th.start();
}
}
class Foo{
//静态方法
public synchronized static void dosth1() {
//获取当前对象的class对象锁才能执行该方法
}
}
3.修饰代码块:所用的锁为某个指定的Java对象
class Counter{
public static int count=0;
public static final Object lock=new Object();
}
//递增线程
class AddThread extends Thread{
@Override
public void run() {
//加锁
synchronized (Counter.lock) {
for(int i=0;i<1000;i++) {
Counter.count+=1;
}
}
}
}
//递减线程
class DecrThread extends Thread{
@Override
public void run() {
//加锁
synchronized (Counter.lock) {
for(int i=0;i<1000;i++) {
Counter.count-=1;
}
}
}
}
ReentrantLock
ReentrantLock和sychronized一样都是可重入锁,但是他在等待释放锁的过程中会有尝试机制。
if(lock.tryLock(1,TimeUnit.SECONDS)) {
lock.lock();
try {
count+=i;
//将释放锁放于finallly里是防止由于异常无法正常释放
}finally {
//释放锁
lock.unlock();
}
}
ReentrantLock实现了Lock接口,Lock接口中定义了lock() unlock()等方法。它有三个内部类Sync、FairSync、NoFairSync。它的构造方法默认传入的是NoFairSync.
public ReentrantLock() {
sync = new NonfairSync();
}
NoFairSync:每一次都尝试获取锁,不会采取公平等待的原则,即使有些线程长时间都没有获取锁,也不会将锁让给该线程。
FairSync:当有空闲资源时先会考虑队列中等待时间最长的线程。
ReentrantLock与synchronized的区别
- synchronized 可以给类、方法、代码块加锁;而ReentrantLock 只能给代码块加锁。
- synchronized 不需要手动获取锁和释放锁,使用简单,发生异常会自动释放锁,不会造成死锁;而ReentrantLock 需要自己加锁和释放锁,如果使用不当没有 unLock()去释放锁就会造成死锁,所以常在finally代码块释放。
- 通过ReentrantLock可以知道有没有成功获取锁,而 synchronized 不能。