>  기사  >  Java  >  Java의 다양한 잠금 메커니즘은 무엇입니까

Java의 다양한 잠금 메커니즘은 무엇입니까

王林
王林앞으로
2023-04-20 08:34:121115검색

머리말

Java의 일반적인 잠금 요약

각 잠금 메커니즘 구별 및 사용 방법

사용 방법 잠금 이름
스레드가 동기화된 리소스를 잠그기를 원하는지 검사하세요 낙관적 잠금 및 비관적 잠금
동기화 리소스를 잠근 후 차단하시겠습니까? 차단하지 않으면 스핀 잠금을 사용할 수 있습니다
하나의 스레드와 여러 프로세스가 동일한 잠금을 획득합니다 재진입 잠금
여러 스레드가 하나의 잠금을 공유함 읽기-쓰기 잠금(쓰기를 위해 공유 잠금)
여러 스레드가 대기열에 들어가기 위해 경쟁해야 할까요 공정한 잠금과 불공정한 잠금

1. 낙관적 잠금과 비관적 잠금

비관적 잠금: 여러 사람이 동시에 실행할 수 없습니다. 이러한 많은 잠금 메커니즘은 행 잠금, 테이블 잠금, 읽기 잠금, 쓰기 잠금 등과 같은 기존 관계형 데이터베이스에서 사용되며 모두 작업 전에 잠깁니다.

낙관적 잠금: 버전 번호가 일치하는지 여부, 즉 , 데이터에 버전 추가, 데이터를 동기식으로 업데이트 및 버전 번호 추가. 잠기지 않고, 버전 번호를 확인할 수 있으며, 인생에서 티켓을 잡는 것과 비슷하게 여러 사람이 조작할 수 있습니다. 데이터를 얻으러 갈 때마다 다른 사람이 수정하지 않을 것이라고 생각하므로 잠기지 않습니다. 그러나 업데이트할 때 이 기간 동안 다른 사람이 데이터를 업데이트했는지 여부를 판단하게 됩니다. 숫자. 낙관적 잠금은 처리량을 향상시킬 수 있는 다중 읽기 애플리케이션 유형에 적합합니다. Redis는 이 검사 및 설정 메커니즘을 사용하여 트랜잭션을 구현합니다.

(낙관적 잠금은 버전 번호 메커니즘 및 CAS 알고리즘을 사용하여 구현할 수 있습니다.)

Java의 다양한 잠금 메커니즘은 무엇입니까

Redis 프레임워크에서

특정 사례를 통해 비관적 잠금과 낙관적 잠금을 보여줍니다.

multi를 실행하기 전에 watch 명령을 실행하세요

구체적인 형식은 다음과 같습니다

watch key1 [key2]

구체적인 코드 형식은 다음과 같습니다

127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> set add 100
OK
127.0.0.1:6379> watch add
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> incrby add 20
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 120
127.0.0.1:6379>

flushdb는 데이터베이스를 삭제하는 것입니다

Java의 다양한 잠금 메커니즘은 무엇입니까

그런데 다른 서버에서 exec를 입력하면, 오류가 표시됩니다

낙관적 잠금이므로 수정 후 버전이 변경됩니다

일반적으로:

비관적 잠금: 모두가 혼자서 작업을 완료할 때 잠금 및 잠금 해제가 실행됩니다. 동시성 문제를 해결하기 위해 동시 작업을 지원하지 않고 하나씩만 작업할 수 있어 비효율적입니다

낙관적 잠금: 무언가가 실행될 때마다 데이터 버전 번호를 비교하여 먼저 제출하는 사람이 버전을 제출합니다. 번호 우선

2. 공정성 잠금 및 불공정 잠금

공정한 잠금: 선착순

불공정한 잠금: 순서가 맞지 않아 대기열이 점프될 수 있음

  • 공정한 잠금: 상대적으로 낮은 효율성

  • 불공평한 잠금: 효율성은 높지만 굶어 죽기 쉽습니다

이 기능을 통해 Lock lock = new ReentrantLock(true);. 재진입 잠금을 생성합니다. true는 공정한 잠금을 의미하고, false는 불공정한 잠금을 의미합니다. 기본 불공정 잠금

소스코드를 보면

매개변수가 포함된 ReentrantLock(true)은 공정 잠금입니다

ReentrantLock(false)은 불공정 잠금입니다

주로 NonfairSync()와 FairSync()를 호출

public ReentrantLock() {
        sync = new NonfairSync();
    }

    /**
     * Creates an instance of {@code ReentrantLock} with the
     * given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

구체적으로는 는 Fair lock이 아니며 Fair lock의 소스코드입니다

Fair Lock의 소스코드 보기

static final class FairSync extends Sync {
   private static final long serialVersionUID = -3000897897090466540L;

  /**
  * Acquires only if reentrant or queue is empty.
   */
  final boolean initialTryLock() {
   Thread current = Thread.currentThread();
   int c = getState();
   if (c == 0) {
   if (!hasQueuedThreads() && compareAndSetState(0, 1)) {
     setExclusiveOwnerThread(current);
      return true;
    }
    } else if (getExclusiveOwnerThread() == current) {
      if (++c < 0) // overflow
          throw new Error("Maximum lock count exceeded");
         setState(c);
         return true;
       }
    return false;
}

코드 예제를 통한 자세한 동작

//第一步  创建资源类,定义属性和和操作方法
class LTicket {
    //票数量
    private int number = 30;

    //创建可重入锁
    private final ReentrantLock lock = new ReentrantLock(true);
    //卖票方法
    public void sale() {
        //上锁
        lock.lock();
        try {
            //判断是否有票
            if(number > 0) {
                System.out.println(Thread.currentThread().getName()+" :卖出"+(number--)+" 剩余:"+number);
            }
        } finally {
            //解锁
            lock.unlock();
        }
    }
}

public class LSaleTicket {
    //第二步 创建多个线程,调用资源类的操作方法
    //创建三个线程
    public static void main(String[] args) {

        LTicket ticket = new LTicket();

new Thread(()-> {
    for (int i = 0; i < 40; i++) {
        ticket.sale();
    }
},"AA").start();

        new Thread(()-> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"BB").start();

        new Thread(()-> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"CC").start();
    }
}

결과 스크린샷은 다음과 같습니다

Java의 다양한 잠금 메커니즘은 무엇입니까

모두 A 스레드에 의해 실행되며, 그러나 BC 스레드가 실행되지 않고 비표준 오류가 발생합니다. Fair lock

재진입 잠금의 매개변수화된 생성자를 통해 해당 설정에 대한 구체적인 변경이 가능합니다.

코드를 비공개로 수정 final ReentrantLock lock = new ReentrantLock(true );

코드 스크린샷은

Java의 다양한 잠금 메커니즘은 무엇입니까

3. 사용 가능한 재진입 잠금

재진입 잠금은 재귀 잠금이라고도 합니다

그리고 재진입 잠금을 사용하면 첫 번째 잠금을 해제한 후 언제든지 내부 구조에 들어갈 수 있습니다

Object o = new Object();
new Thread(()->{
    synchronized(o) {
        System.out.println(Thread.currentThread().getName()+" 外层");

        synchronized (o) {
            System.out.println(Thread.currentThread().getName()+" 中层");

            synchronized (o) {
                System.out.println(Thread.currentThread().getName()+" 内层");
            }
        }
    }

},"t1").start();

동기화(o)는 현재 { }를 잠그는 것을 의미합니다. 내부의 코드 블록

위는 모두 동기화된 잠금 메커니즘입니다

잠금 잠금 메커니즘은 아래에 설명되어 있습니다

public class SyncLockDemo {

    public synchronized void add() {
        add();
    }

    public static void main(String[] args) {
        //Lock演示可重入锁
        Lock lock = new ReentrantLock();
        //创建线程
        new Thread(()->{
            try {
                //上锁
                lock.lock();
                System.out.println(Thread.currentThread().getName()+" 外层");

                try {
                    //上锁
                    lock.lock();
                    System.out.println(Thread.currentThread().getName()+" 内层");
                }finally {
                    //释放锁
                    lock.unlock();
                }
            }finally {
                //释放做
                lock.unlock();
            }
        },"t1").start();

        //创建新线程
        new Thread(()->{
            lock.lock();
            System.out.println("aaaa");
            lock.unlock();
        },"aa").start();
        }
 }

동일한 잠금의 중첩 잠금에서 내부 중첩 잠금은 언락되지 않으면 출력되지만, 쓰레드에서 뛰어내리면 다른 쓰레드를 실행하면 교착상태가 발생합니다

락과 언락의 개념을 이해하려면

Java의 다양한 잠금 메커니즘은 무엇입니까

이라고 써야 합니다.4. 잠금(공유 잠금 및 배타적 잠금)

읽기 잠금은 공유 잠금, 쓰기 잠금은 배타적 잠금

  • 공유 잠금의 특정 구현

  • 읽기-쓰기 잠금은 일련의 잠금을 관리하며 하나는 읽기입니다. - 잠금만 있고 다른 하나는 쓰기 잠금입니다.

읽기-쓰기 잠금: 여러 읽기 스레드 또는 하나의 쓰기 스레드로 리소스에 액세스할 수 있지만 읽기 및 쓰기 스레드는 동시에 존재할 수 없으며 읽기-쓰기 상호 배제, 읽기-읽기 공유(쓰기 잠금 독점) , 읽기 잠금 공유), 쓰기 잠금 우선순위가 읽기 잠금보다 높습니다)

읽기-쓰기 잠금 ReentrantReadWriteLock

읽기 잠금은 ReentrantReadWriteLock.ReadLock, readLock() 메서드

쓰기 잠금은 ReentrantReadWriteLock.WriteLock, writeLock() 메서드

읽기-쓰기 잠금 개체 만들기 private ReadWriteLock rwLock = new ReentrantReadWriteLock();

쓰기 잠금 rwLock.writeLock().lock();, 잠금 해제 rwLock.writeLock().unlock();

읽기 잠금 rwLock.readLock() .lock();, 잠금 해제는 rwLock.readLock().unlock();

사례 분석:

멀티스레딩을 시뮬레이션하여 지도에서 데이터를 가져오고 읽습니다

완전한 코드는 다음과 같습니다

//资源类
class MyCache {
    //创建map集合
    private volatile Map<String,Object> map = new HashMap<>();

    //创建读写锁对象
    private ReadWriteLock rwLock = new ReentrantReadWriteLock();

    //放数据
    public void put(String key,Object value) {
        //添加写锁
        rwLock.writeLock().lock();

        try {
            System.out.println(Thread.currentThread().getName()+" 正在写操作"+key);
            //暂停一会
            TimeUnit.MICROSECONDS.sleep(300);
            //放数据
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+" 写完了"+key);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //释放写锁
            rwLock.writeLock().unlock();
        }
    }

    //取数据
    public Object get(String key) {
        //添加读锁
        rwLock.readLock().lock();
        Object result = null;
        try {
            System.out.println(Thread.currentThread().getName()+" 正在读取操作"+key);
            //暂停一会
            TimeUnit.MICROSECONDS.sleep(300);
            result = map.get(key);
            System.out.println(Thread.currentThread().getName()+" 取完了"+key);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //释放读锁
            rwLock.readLock().unlock();
        }
        return result;
    }
}

public class ReadWriteLockDemo {
    public static void main(String[] args) throws InterruptedException {
        MyCache myCache = new MyCache();
        //创建线程放数据
        for (int i = 1; i <=5; i++) {
            final int num = i;
            new Thread(()->{
                myCache.put(num+"",num+"");
            },String.valueOf(i)).start();
        }

        TimeUnit.MICROSECONDS.sleep(300);

        //创建线程取数据
        for (int i = 1; i <=5; i++) {
            final int num = i;
            new Thread(()->{
                myCache.get(num+"");
            },String.valueOf(i)).start();
        }
    }
}

5. 뮤텍스 잠금

상호 배제 잠금은 특정 리소스에 대해 동시에 한 명의 방문자만 액세스할 수 있도록 허용하는 고유한 잠금입니다.

pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;//创建互斥锁并初始化

pthread_mutex_lock(&mutex);//对线程上锁,此时其他线程阻塞等待该线程释放锁

//要执行的代码段

pthread_mutex_unlock(&mutex);//执行完后释放锁

6.Baidu Encyclopedia를 참조하세요. 자세한 내용은 다음과 같습니다.

它是为实现保护共享资源而提出一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名

通俗的来说就是一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。获取锁的线程一直处于活跃状态,但是并没有执行任何有效的任务。

其特点:

  1. 持有锁时间等待过长,消耗CPU

  2. 无法满足等待时间最长的线程优先获取锁。不公平的锁就会存在“线程饥饿”问题

  3. 自旋锁不会使线程状态发生切换,处于用户态(不会到内核态进行线程的状态转换),一直都是活跃,不会使线程进入阻塞状态,减少了不必要的上下文切换,执行速度快。

其模拟算法如下

do{
	b=1;
	while(b){
		lock(bus);
		b = test_and_set(&lock);
		unlock(bus);
	}
	//临界区
	//lock = 0;
	//其余部分
}while(1)

7. 无锁 / 偏向锁 / 轻量级锁 / 重量级锁

  • 无锁:没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功

  • 偏向锁:是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁,降低获取锁的代价

  • 轻量级锁:锁是偏向锁的时候,被另外的线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,从而提高性能

  • 重量级锁:线程并发加剧,线程的自旋超过了一定次数,或者一个线程持有锁,一个线程在自旋,还有线程要访问

위 내용은 Java의 다양한 잠금 메커니즘은 무엇입니까의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제