JAVA多线程-读写锁

JAVA读写锁:前面讲到了可重入锁,虽然使用方面会比synchronized性能高,但是还是没有达到我们理想得要求,我们实际项目中,比如同时访问写的时候需要互斥,或者写的时候也读也需要等待,但是多个线程同时读得时候,不需要等待,这时候就要用到读写锁了ReadWriteLock,我们先看下ReadWriteLock接口:

public interface ReadWriteLock {

    /**
     * 返回一个读锁
     */

    Lock readLock();

    /**
     * 返回一个写锁
     */

    Lock writeLock();
}

我们看下读写锁得用法,首先同时进行写,我们看下账户类:

package com.ck.thread;

import java.math.BigDecimal;

import java.util.concurrent.locks.ReadWriteLock;

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Account {

    private ReadWriteLock lock = new ReentrantReadWriteLock();

    private BigDecimal balnace = new BigDecimal("100");

    //充值

    public void add(BigDecimal amount ) throws InterruptedException {

        //写锁

        lock.writeLock().lock();

        System.out.println(" 开始充值,线程名: " + Thread.currentThread().getName());

        try {

            Thread.sleep(2000);

            balnace = balnace.add(amount);

            System.out.println(" 结束充值,线程名: " + Thread.currentThread().getName());

        } catch (InterruptedException e) {

            e.printStackTrace();

        }finally {

            lock.writeLock().unlock();

        }

    }
 

    //查询余额

    public void getBalace() {

        //读锁

        lock.readLock().lock();

        try {

            System.out.println("开始查询线程名: " + Thread.currentThread().getName() );

            try {

                Thread.sleep(2000);

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

            System.out.println("查询结果线程名: " + Thread.currentThread().getName() +", 余额:" + balnace);

        }finally {

            lock.readLock().unlock();

        }

    }  

}

然后充值类:

package com.ck.thread;

import java.math.BigDecimal;

public class CzThread extends Thread{

    private Account account;

    public CzThread(Account account) {

        this.account = account;

    }


    @Override

    public void run() {

        try {

            account.add(new BigDecimal("100"));

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

    }

    public Account getAccount() {

        return account;

    }

    public void setAccount(Account account) {

        this.account = account;

    }

}

获取余额线程:

package com.ck.thread;

public class GetBalaceThread extends Thread{

    private Account account;

    public GetBalaceThread(Account account) {

        super();

        this.account = account;

    }


    @Override

    public void run() {

        account.getBalace();

    }

  
    public Account getAccount() {

        return account;

    }

    public void setAccount(Account account) {

        this.account = account;

    }

}

主线程方法:

package com.ck.thread;





public class MainThread {

    public static void main(String[] args) throws InterruptedException {

        Account account = new Account();

       

        CzThread cz1 = new CzThread(account);

        cz1.setName("thread1");

        cz1.start();

       

        CzThread cz2 = new CzThread(account);

        cz2.setName("thread2");

        cz2.start();

    }

}

执行结果:

开始充值,线程名: thread2

 结束充值,线程名: thread2

 开始充值,线程名: thread1

 结束充值,线程名: thread1

我们通过结果可以看到,因为充值得线程都调用了Accountadd方法,该方法上加了写的锁,所以都需要等待,我们看到线程2执行完了才执行线程1,说明写锁是互斥的,我们继续看些读写的情况,修改主线程如下:

public class MainThread {


    public static void main(String[] args) throws InterruptedException {

        Account account = new Account();

       
        CzThread cz1 = new CzThread(account);

        cz1.setName("thread1");

        cz1.start();
       

        GetBalaceThread cz2 = new GetBalaceThread(account);

        cz2.setName("thread2");

        cz2.start();

    }

}

执行结果:

开始充值,线程名: thread1

 结束充值,线程名: thread1

开始查询线程名: thread2

查询结果线程名: thread2, 余额:200

通过结果我们可以看到,先执行充值的情况下,获取余额线程需要等待,说明读写锁也是相互互斥的,下面我们继续看下读读锁,修改主线程如下:

public class MainThread {


    public static void main(String[] args) throws InterruptedException {

        Account account = new Account();

        GetBalaceThread cz1 = new GetBalaceThread(account);

        cz1.setName("thread1");

        cz1.start();

      
        GetBalaceThread cz2 = new GetBalaceThread(account);

        cz2.setName("thread2");

        cz2.start();

    }

}

执行结果:

开始查询线程名: thread1

开始查询线程名: thread2

查询结果线程名: thread1, 余额:100

查询结果线程名: thread2, 余额:100

通过结果可以看到,读锁直接不存在竞争锁的关系,可以同时访问一个方法。

总结:读写锁可以提供程序并发的性能,而且比较灵活,具体原理我们后续会继续介绍。

你可能感兴趣的:(JAVA并发编程,java)