线程锁

86 阅读3分钟

可重入锁

也叫可递归锁,当同一线程再次进入同步代码时,可以使用自己已获取到的锁;其目的是当同一线程多次获取到的是同一把锁能防止死锁的发生

代码展示

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockTest implements Runnable {

    private Lock lock = new ReentrantLock();

    public void get() {

        lock.lock();

        System.out.println("get方法的线程id: " + Thread.currentThread().getId());

        set();

        lock.unlock();

    }

    public void set() {

        lock.lock();

        System.out.println("set方法的线程id: " + Thread.currentThread().getId());

        lock.unlock();

    }

    @Override
    public void run() {

        get();

    }

    public static void main(String[] args) {

        LockTest lockTest = new LockTest();

        new Thread(lockTest).start();

        new Thread(lockTest).start();

    }
}

打印展示

可重入锁

公平锁与非公平锁

公平锁与非公平锁

1.公平锁会维护一个等待队列,多个在阻塞状态等待的线程会被插入到等待队列,在调度时是按所发请求的时间顺序获取锁
2.在无需保证先进先出的情况下,可以使用非公平锁去抢占锁
3.非公平锁性能高于公平锁,但是如果当前线程不是队列的第一个线程就无法获取到锁,这样会增加线程的切换次数
4.如果线程占用(处理)时间远长于线程等待时间,那么使用非公平锁的效率就不太明显,但是使用公平锁会增强业务的可控性

公平锁

代码展示

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockTest {

    /* 公平锁 */
    private Lock lock = new ReentrantLock(true);

    public void fairLock() {

        try {

            lock.lock();

            System.out.println(Thread.currentThread().getName() + "获得了锁");

        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {

        LockTest lockTest = new LockTest();

        Runnable runnable = () -> {

            System.out.println(Thread.currentThread().getName() + "启动");

            lockTest.fairLock();

        };

        Thread[] threads = new Thread[10];

        for (int i = 0; i < 10; i++) threads[i] = new Thread(runnable);

        for (int i = 0; i < 10; i++) threads[i].start();

    }
}

打印展示

公平锁

非公平锁

代码展示

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockTest {

    /* 非公平锁 */
    private Lock lock = new ReentrantLock(false);

    public void fairLock() {

        try {

            lock.lock();

            System.out.println(Thread.currentThread().getName() + "获得了锁");

        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {

        LockTest lockTest = new LockTest();

        Runnable runnable = () -> {

            System.out.println(Thread.currentThread().getName() + "启动");

            lockTest.fairLock();

        };

        Thread[] threads = new Thread[10];

        for (int i = 0; i < 10; i++) threads[i] = new Thread(runnable);

        for (int i = 0; i < 10; i++) threads[i].start();

    }
}

打印展示

非公平锁

读写锁

1.ReentrantReadWriteLock(读写锁)对象会使用两把锁管理临界资源,分别是读锁与写锁
2.某线程获得了资源的读锁,其他读操作可以并发,但写操作的线程会被阻塞
3.当读操作的数量远超过写操作时,可以用读写锁使读操作能够并发执行,从而提升性能

代码展示

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class LockTest {

    /* 读写锁 */
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    /* 读锁 */
    private final Lock readLock = lock.readLock();
    /* 写锁 */
    private final Lock writeLock = lock.writeLock();

    private int num;

    public void read() {

        int i = 0;

        while (i++ < 3) {

            try {
                readLock.lock();
                System.out.println(Thread.currentThread().getId() + " 开始读");
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getId() + " 读num: " + num);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                readLock.unlock();
            }
        }
    }

    public void write() {

        int i = 0;

        while (i++ < 3) {

            try {
                writeLock.lock();
                System.out.println(Thread.currentThread().getId() + " 开始写");
                Thread.sleep(1000);
                num = (int) (Math.random() * 10);
                System.out.println(Thread.currentThread().getId() + " 修改后的num: " + num);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                writeLock.unlock();
            }
        }
    }

    public static class ReadThead extends Thread {

        private final LockTest lockTest;

        public ReadThead(LockTest lockTest) {
            this.lockTest = lockTest;
        }

        @Override
        public void run() {
            lockTest.read();
        }
    }

    public static class WriteThead extends Thread {

        private final LockTest lockTest;

        public WriteThead(LockTest lockTest) {
            this.lockTest = lockTest;
        }

        @Override
        public void run() {
            lockTest.write();
        }
    }

    public static void main(String[] args) {

        LockTest lockTest = new LockTest();

        for (int i = 0; i < 3; i++) {
            new ReadThead(lockTest).start();
            new WriteThead(lockTest).start();
        }

    }
}

打印展示

12 开始读
12 读num: 0
13 开始写
13 修改后的num: 5
13 开始写
13 修改后的num: 1
13 开始写
13 修改后的num: 7
15 开始写
15 修改后的num: 5
15 开始写
15 修改后的num: 3
15 开始写
15 修改后的num: 4
14 开始读
16 开始读
14 读num: 4
16 读num: 4
17 开始写
17 修改后的num: 2
17 开始写
17 修改后的num: 3
17 开始写
17 修改后的num: 2
12 开始读
14 开始读
16 开始读
14 读num: 2
16 读num: 2
16 开始读
12 读num: 2
12 开始读
14 开始读
14 读num: 2
16 读num: 2
12 读num: 2

Process finished with exit code 0