Java笔记2.2--线程之锁的使用

ReentrantLock:

控制业务层面的并发。案例:ATM登录,查询,取钱,退出等整个流程只能有一个人操作。

class AccountWithLock{
    private Lock lock;
    public AccountWithLock(){
        lock = new ReentrantLock();
    }
    //锁账户
    public void lockAccount(){
        lock.lock();
    }
    //解锁账户
    public void unLockAccount(){
        lock.unlock();
    }
    public void login(){
        //TODO
    }
    public void logout(){
        //TODO
    }
    public void add(){
        //TODO
    }
    public static void main(String[] args){
        AccountWithLock acc = new AccountWithLock();
        acc.lockAccount();
        acc.login();
        acc.add();
        acc.logout();
        acc.unLockAccount();
    }
}

ReentrantLock和synchronized一样,是可重入锁(可递归锁),同一线程再次进入同步代码时可以使用已经获得的锁。同一线程多次获取同一把锁,防止死锁发生。

场景:数据库的操作线程需要多次调用被锁管理的“获取数据库连接”的方法,如果使用可重入锁可避免思索,反之在第二次调用“获取数据库连接”的方法时候可能被锁住。

重入锁代码示例:

class LockReEnTer implements Runnable{
    ReentrantLock lock = new ReentrantLock();

    public void get(){
        lock.lock();
        System.out.println(Thread.currentThread().getId() + "\t");
        set();
        lock.unlock();
    }

    public void set(){
        lock.lock();
        System.out.println(Thread.currentThread().getId() + "\t");
        lock.unlock();
    }

    public void run(){
        get();
    }

    public static void main(String[] args){
        LockReEnTer demo = new LockReEnTer();
        new Thread(demo).start();
        new Thread(demo).start();
    }
}

输出为:12 12 13 13

输出表明,在某个线程中,当他获得这个锁,如果又再次加锁,实际上还是进入了原来获得的那个锁,因为线程还是这个线程,锁是可重入的,不会新获得一个锁。

公平锁和非公平锁:

1.公平锁会维护一个等待队列,多个在阻塞状态等待的线程被插入该等待队列,调度时候按发起请求的时间顺序获取锁。

2.非公平锁未必保证先来先服务,非公平锁有更多的机会去抢占锁。

3.创建可重入锁时,通过调用boolean类型参数的构造函数指定是否为公平锁(ReentarantLock(boolean fair))

4.非公平锁的性能高于公平锁,当前线程不是队列的第一个就无法获取锁,从而增加了线程的切换次数。

5.如果线程占用(处理)时间远长于线程等待,那非公平锁的效率不明显,用公平锁会给业务增加可控制性。

公平锁示例代码:

public class FairLockDemo {
    /**
     * true为公平锁
     */
    private ReentrantLock lock = new ReentrantLock(true);

    public void testFair(){
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "获得了锁");
        }finally {
            lock.unlock();
        }
    }
    public static void main(String[] args){
        FairLockDemo fairLockDemo = new FairLockDemo();
        Runnable runnable = () ->{
            System.out.println(Thread.currentThread().getName() + "启动");
            fairLockDemo.testFair();
        };
        Thread[] threadsArray = new Thread[10];
        for (int i = 0; i < 10; ++i){
            threadsArray[i] = new Thread(runnable);
        }
        for (int i = 0; i < 10; ++i){
            threadsArray[i].start();
        }
    }
}

输出:
Thread-0启动
Thread-5启动
Thread-0获得了锁
Thread-4启动
Thread-1启动
Thread-3启动
Thread-2启动
Thread-9启动
Thread-8启动
Thread-5获得了锁
Thread-7启动
Thread-6启动
Thread-4获得了锁
Thread-1获得了锁
Thread-3获得了锁
Thread-2获得了锁
Thread-9获得了锁
Thread-8获得了锁
Thread-7获得了锁
Thread-6获得了锁

非公平锁结果:(将true改为false)

Thread-0启动
Thread-3启动
Thread-2启动
Thread-1启动
Thread-6启动
Thread-0获得了锁
Thread-4启动
Thread-4获得了锁
Thread-5启动
Thread-7启动
Thread-5获得了锁
Thread-8启动
Thread-8获得了锁
Thread-2获得了锁
Thread-9启动
Thread-3获得了锁
Thread-1获得了锁
Thread-6获得了锁
Thread-7获得了锁
Thread-9获得了锁

读写锁:

1.ReentrantReadWriteLock对象使用两把锁来管理临界资源(一个是读锁,一个是写锁)。从名字可以看出来也是可重入锁。

2.某线程获得了资源的“读锁”,其他读操作可以并发,但是写操作的线程会被阻塞。

3.某线程获得了资源的“写锁”,其他任何想要获得该资源“读锁”和“写锁”的进程都会被阻塞。

4.如果读操作数量远超过写操作时,可以用该读写锁让读操作并发执行,提升性能。

示例代码:

class ReadWriteTool{
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    private Lock readLock = lock.readLock();
    private Lock writeLock = lock.writeLock();
    private int num = 0;

    public void read(){
        int cnt = 0;
        while (cnt++ < 3){
            try {
                readLock.lock();
                System.out.println(Thread.currentThread().getId() + " start to read");
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getId() + "reading," + num);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                readLock.unlock();
            }
        }
    }

    public void write(){
        int cnt = 0;
        while (cnt++ < 3){
            try {
                writeLock.lock();
                System.out.println(Thread.currentThread().getId() + " start to write");
                Thread.sleep(1000);
                num = (int) (Math.random() * 10);
                System.out.println(Thread.currentThread().getId() + "write," + num);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                writeLock.unlock();
            }
        }
    }
}

class ReadThread extends Thread{
    private ReadWriteTool readTool;

    public ReadThread(ReadWriteTool readTool){
        this.readTool = readTool;
    }

    public void run(){
        readTool.read();
    }
}

class WriteThread extends Thread{
    private ReadWriteTool writeTool;

    public WriteThread(ReadWriteTool writeTool){
        this.writeTool = writeTool;
    }

    public void run(){
        writeTool.write();
    }
}

public class ReadWriteLockDemo {
    public static void main(String[] args){
        ReadWriteTool tool = new ReadWriteTool();
        for (int i = 0; i < 3; ++i){
            new ReadThread(tool).start();
            new WriteThread(tool).start();
        }
    }
}

运行结果:
12 start to read
14 start to read
12reading,0
14reading,0
13 start to write
13write,1
13 start to write
13write,2
13 start to write
13write,6
15 start to write
15write,7
15 start to write
15write,4
15 start to write
15write,4
17 start to write
17write,9
17 start to write
17write,2
17 start to write
17write,6
16 start to read
12 start to read
14 start to read
16reading,6
16 start to read
14reading,6
14 start to read
12reading,6
12 start to read
14reading,6
16reading,6
16 start to read
12reading,6
16reading,6

从结果可以看出,读操作可以并行,写操作是不可以的,一旦某个线程进入写锁,那么其余的线程都不能获得任何锁。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值