java 锁

89 阅读3分钟

synchronized

  • 对象锁
    • 锁this对象:synchronized(this) { }
    • 锁非静态变量:synchronized(object) { }
    • 锁非静态方法:private synchronized void syncMethod( ){ }
  • 类锁
    • 锁类的class:synchronized(类.class) { }
    • 锁静态变量:static Object object;synchronized(object) { }
    • 锁静态方法:private synchronized static void syncMethod( ){ }

一个类只有一个类锁。一个类可以创建很多对象,每个对象都有各自的对象锁,对象锁间互不影响。
对象锁和类锁也互不影响。

对象锁

@Slf4j
public class SyncThread implements Runnable {

    private final Object obj = new Object();  //非静态变量

    @Override
    public void run() {
        syncMethod();
    }

    private void syncMethod() {
        log.info("thread in");
        synchronized (obj) {  //获取非静态变量的锁
            try {
                log.info("thread start");
                TimeUnit.SECONDS.sleep(2);
                log.info("thread end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
class Main {
    public static void main(String[] args) {
        SyncThread syncThread = new SyncThread();
        new Thread(new SyncThread(), "thread-1").start();
        new Thread(new SyncThread(), "thread-2").start();
        new Thread(syncThread, "thread-3").start();
        new Thread(syncThread, "thread-4").start();
    }
}

thread-1和thread-2是生成了两个SyncThread对象,这两个SyncThread对象又有各自的Object对象,所以thread-1和thread-2可以获取各自的对象锁,互不影响。
thread-3和thread-4公用一个SyncThread对象,共同竞争一个SyncThread对象的Object对象的锁,存在互斥。
thread-1,thread-2,thread-3或thread-1,thread-2,thread-4间是不同的对象锁,互不影响。

image.png

@Slf4j
public class SyncThread implements Runnable {

    @Override
    public void run() {
        syncMethod();
    }

    private void syncMethod() {
        log.info("thread in");
        synchronized (this) {  // 获取this对象的锁
            try {
                log.info("thread start");
                TimeUnit.SECONDS.sleep(2);
                log.info("thread end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

thread-1和thread-2获取各自匿名对象SyncThread对象的锁,是当前线程类的对象的锁,而在上面的非静态变量对象锁,是当前线程类的对象的非静态实例变量的对象锁,多了一层,效果一样。

@Slf4j
public class SyncThread implements Runnable {

    @Override
    public void run() {
        syncMethod();
    }

    private synchronized void syncMethod() {  // 非静态方法的对象锁
        log.info("thread in");
        try {
            log.info("thread start");
            TimeUnit.SECONDS.sleep(2);
            log.info("thread end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

这种对象锁应该和this锁一样,都是当前线程类的对象的锁。

类锁

@Slf4j
public class SyncThread implements Runnable {

    @Override
    public void run() {
        syncMethod();
    }

    private void syncMethod() {
        log.info("thread in");
        synchronized (SyncThread.class) {
            try {
                log.info("thread start");
                TimeUnit.SECONDS.sleep(2);
                log.info("thread end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

thread-1,thread-2,thread-3,thread-4的线程对象都是同一个类创建的,他们竞争同一个类锁,所以这4个线程都存在互斥关系。

image.png

@Slf4j
public class SyncThread implements Runnable {

    private static final Object lock = new Object();  //静态变量

    @Override
    public void run() {
        syncMethod();
    }

    private void syncMethod() {
        log.info("thread in");
        synchronized (lock) {  //获取静态变量的锁
            try {
                log.info("thread start");
                TimeUnit.SECONDS.sleep(2);
                log.info("thread end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

因为静态变量是类变量,各个对象共有,所以静态变量的锁也是类锁,效果和上面一样。

@Slf4j
public class SyncThread implements Runnable {

    @Override
    public void run() {
        syncMethod();
    }

    private synchronized static void syncMethod() {  //获取静态方法的锁
        log.info("thread in");
        try {
            log.info("thread start");
            TimeUnit.SECONDS.sleep(2);
            log.info("thread end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

静态方法和静态变量一样,都是类的,不是某个对象的,所以是类锁,同样效果和上面一样。

线程通信

class Main {
    public static void main(String[] args) throws InterruptedException{
        Object obj = new Object();
        for (int i = 0; i < 5; i++) {
            new Thread(()->{
                synchronized (obj){
                    System.out.println("thread start");
                    try {
                        obj.wait();  //放弃锁,自我阻塞
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println("thread end");
                }
            }).start();
        }
        System.out.println("main start");
        Thread.sleep(2000);
        synchronized (obj){  //申请锁资源
            obj.notifyAll();  //唤醒其他所有线程,这些线程再竞争锁资源
,        }
        System.out.println("main end");
    }
}

ReentrantLock

ReentrantLock lock = new ReentrantLock();
for (int i = 0; i < 5; i++) {
    new Thread(()->{
        try {
            lock.lock();
            System.out.println("hello");
            Thread.sleep(2000);
            lock.unlock();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }).start();
}
ReentrantLock lock = new ReentrantLock();
for (int i = 0; i < 5; i++) {
    new Thread(()->{
        boolean b=false;
        try {
            b=lock.tryLock();
            if (b){
                System.out.println("hello");
                Thread.sleep(2000);
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }finally {
            if (b){
                lock.unlock();
            }
        }
    }).start();
}

tryLock()会试图去获取锁,如果获取成功返回true,如果获取失败,则放弃并返回false,并且不会阻塞。
还可以传入时间,表示最多等待的时间。

线程通信

ReentrantLock lock = new ReentrantLock();
Condition c1 = lock.newCondition();
for (int i = 0; i < 5; i++) {
    new Thread(()->{
        try {
            lock.lock();
            c1.await();  //放弃锁,自我阻塞
            System.out.println("hello");
            Thread.sleep(1000);
            lock.unlock();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }).start();
}
Thread.sleep(2000);
lock.lock();  //先拥有锁资源
c1.signalAll();  //唤醒其他所有线程
lock.unlock();

可以创建多个Condition完成较复杂的通信

可重入

image.png