java并发锁

发布时间 2023-07-24 16:15:36作者: _Explosion!

关于锁的种类介绍可见https://www.cnblogs.com/dennyLee2025/p/16600426.html

下面记录几种java中常用到的并发锁

synchronized

synchronized是java中的一个关键字,被其修饰的方法或代码块在任意时刻只能被一个线程执行。

对象锁:java中每个类对象都存在一个锁对象,该锁被称为对象锁。类中被synchronized修饰的方法执行前会将对象锁上锁,从而使得同一时刻同个对象只能执行一个被synchronized修饰的方法。

类锁:java中每个都存在一个类的Class对象锁,该锁被称为类锁,针对类而非对象。(相当于静态成员)

  • 获取对象锁

    • synchronized(this|object) {}

    • 修饰非静态方法

  • 获取类锁

    • synchronized(类.class) {}

    • 修饰静态方法

简单用例1(直接获取对象锁):

class MyRunnable implements Runnable{

    @Override
    public void run(){
     //获取对象锁
synchronized(this){ try{ int i=(int)Thread.currentThread().threadId(); System.out.println("this is " + i); Thread.sleep(1000); System.out.println("finish " + i); }catch(Exception e){ e.printStackTrace(); } } } } public class synchronizedTest { public static void main(String[] Args){ Runnable myRunnable = new MyRunnable(); Thread[] test = new Thread[5]; for(int i=0;i<5;++i)test[i]=new Thread(myRunnable); var startTime = System.currentTimeMillis(); for(int i=0;i<5;++i)test[i].start(); try{ for(int i=0;i<5;++i)test[i].join(); }catch(Exception e){ } var endTime = System.currentTimeMillis(); System.out.println("spend " + (endTime - startTime)); } }

输出如下:

 

简单用例2(synchronized 方法):

public class synchronizedTest {
    private static int i = 0;
    private static synchronized void inc(){
        i++;
        System.out.println(i);
    }

    public static void main(String[] Args){
        Thread[] test = new Thread[5];
        for(int i=0;i<5;++i)test[i]=new Thread(new Runnable() {
            @Override
            public void run(){
                inc();
            }
        });
        for(int i=0;i<5;++i)test[i].start();
    }
}

输出如下:

 若上面inc()方法去除synchronized关键字,则输出如下(结果不唯一):

 可见synchronized关键字可有效保证数据的原子性。

 

wait,notify,notifyAll

Object对象wait,notify,notifyAll方法为public final

此三方法只能被持有对应对象对象锁线程调用,否则会抛出异常

wait方法会让当前线程挂起并释放对象锁。

notify方法会随机唤醒一个被wait方法挂起的线程。

notifyAll会唤醒所有被wait方法挂起的线程。

 

需注意:

synchronized 关键字修饰静态方法获得的锁是类锁,反之获得的是对象锁

synchronized 关键字修饰的方法,其synchronized特性不会被继承。即父类中synchronized方法在子类中若不覆盖,则默认无synchronized修饰。

定义接口方法或构造方法时无法使用synchronized修饰

synchronized与volatile的区别类似于c++中std::atomic和volatile的区别(见之前文章)。只不过synchronized无法修饰变量,而volatile只能修饰实例变量。

synchronized是可重入锁

 


Lock接口

Lock接口是java提供的锁接口,提供了以下方法:

  1. lock();  获取锁,如果锁不可用,则出于线程调度的目的,当前线程将被禁用,并且在获取锁之前处于休眠状态。
  2. tryLock();  试图获取锁,根据获取是否成功返回boolean,该方法不会让线程陷入等待。
  3. tryLock(long time, TimeUnit unit);  同2,但是会等待参数时间,之后返回结果。
  4. lockInterruptibly();  试图获取锁,失败则抛出异常InterruptedException
  5. unLock();  释放锁

 继承该接口的常用实现类有

ReentrantLock(可重入锁)
ReentrantReadWriteLock(读写锁) 

  • writeLock():获取写锁。
  • readLock():获取读锁。