博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
复习基础-Java多线程ReentrantLock和ReadWriteLock
阅读量:5867 次
发布时间:2019-06-19

本文共 4863 字,大约阅读时间需要 16 分钟。

  hot3.png

.util.concurrent.lock 中的Lock 框架是锁定的一个抽象,它允许把锁定的实现作为 Java 类,而不是作为语言的特性来实现。这就为Lock 的多种实现留下了空间,各种实现可能有不同的调度、性能特性或者锁定语义。

ReentrantLock 类实现了Lock ,它拥有与synchronized 相同的并发性和内存语义,但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能。(换句话说,当许多线程都想访问共享资源时,JVM 可以花更少的时候来调度线程,把更多时间用在执行线程上。)

class Outputter1 {    

    private Lock lock = new ReentrantLock();// 锁对象    
  
    public void output(String name) {           
        lock.lock();      // 得到锁    
  
        try {    
            for(int i = 0; i < name.length(); i++) {    
                System.out.print(name.charAt(i));    
            }    
        } finally {    
            lock.unlock();// 释放锁    
        }    
    }    
}  

 

区别:

需要注意的是,用sychronized修饰的方法或者语句块在代码执行完之后锁自动释放,而是用Lock需要我们手动释放锁,所以为了保证锁最终被释放(发生异常情况),要把互斥区放在try内,释放锁放在finally内!!

 

上例中展示的是和synchronized相同的功能,那Lock的优势在哪里?

例如一个类对其内部共享数据data提供了get()和set()方法,如果用synchronized,则代码如下:

class syncData {        

    private int data;// 共享数据        
    public synchronized void set(int data) {    
        System.out.println(Thread.currentThread().getName() + "准备写入数据");    
        try {    
            Thread.sleep(20);    
        } catch (InterruptedException e) {    
            e.printStackTrace();    
        }    
        this.data = data;    
        System.out.println(Thread.currentThread().getName() + "写入" + this.data);    
    }       
    public synchronized  void get() {    
        System.out.println(Thread.currentThread().getName() + "准备读取数据");    
        try {    
            Thread.sleep(20);    
        } catch (InterruptedException e) {    
            e.printStackTrace();    
        }    
        System.out.println(Thread.currentThread().getName() + "读取" + this.data);    
    }    
}  

 

然后写个测试类来用多个线程分别读写这个共享数据:

public static void main(String[] args) {    

//        final Data data = new Data();    
          final syncData data = new syncData();    
//        final RwLockData data = new RwLockData();    
          
        //写入  
        for (int i = 0; i < 3; i++) {    
            Thread t = new Thread(new Runnable() {    
                 
        public void run() {    
                    for (int j = 0; j < 5; j++) {    
                        data.set(new Random().nextInt(30));    
                    }    
                }    
            });  
            t.setName("Thread-W" + i);  
            t.start();  
        }    
        //读取  
        for (int i = 0; i < 3; i++) {    
            Thread t = new Thread(new Runnable() {    
                 
        public void run() {    
                    for (int j = 0; j < 5; j++) {    
                        data.get();    
                    }    
                }    
            });    
            t.setName("Thread-R" + i);  
            t.start();  
        }    
    }    

运行结果:

Thread-W0准备写入数据  
Thread-W0写入0  
Thread-W0准备写入数据  
Thread-W0写入1  
Thread-R1准备读取数据  
Thread-R1读取1  
Thread-R1准备读取数据  
Thread-R1读取1  
Thread-R1准备读取数据  
Thread-R1读取1  
Thread-R1准备读取数据  
Thread-R1读取1  
Thread-R1准备读取数据  
Thread-R1读取1  
Thread-R2准备读取数据  
Thread-R2读取1  
Thread-R2准备读取数据  
Thread-R2读取1  
Thread-R2准备读取数据  
Thread-R2读取1  
Thread-R2准备读取数据  
Thread-R2读取1  
Thread-R2准备读取数据  
Thread-R2读取1  
Thread-R0准备读取数据 //R0和R2可以同时读取,不应该互斥!  
Thread-R0读取1  
Thread-R0准备读取数据  
Thread-R0读取1  
Thread-R0准备读取数据  
Thread-R0读取1  
Thread-R0准备读取数据  
Thread-R0读取1  
Thread-R0准备读取数据  
Thread-R0读取1  
Thread-W1准备写入数据  
Thread-W1写入18  
Thread-W1准备写入数据  
Thread-W1写入16  
Thread-W1准备写入数据  
Thread-W1写入19  
Thread-W1准备写入数据  
Thread-W1写入21  
Thread-W1准备写入数据  
Thread-W1写入4  
Thread-W2准备写入数据  
Thread-W2写入10  
Thread-W2准备写入数据  
Thread-W2写入4  
Thread-W2准备写入数据  
Thread-W2写入1  
Thread-W2准备写入数据  
Thread-W2写入14  
Thread-W2准备写入数据  
Thread-W2写入2  
Thread-W0准备写入数据  
Thread-W0写入4  
Thread-W0准备写入数据  
Thread-W0写入20  
Thread-W0准备写入数据  
Thread-W0写入29

现在一切都看起来很好!各个线程互不干扰!等等。。读取线程和写入线程互不干扰是正常的,但是两个读取线程是否需要互不干扰??

我们可以用读写锁ReadWriteLock实现:

class Data {        

    private int data;// 共享数据    
    private ReadWriteLock rwl = new ReentrantReadWriteLock();       
    public void set(int data) {    
        rwl.writeLock().lock();// 取到写锁    
        try {    
            System.out.println(Thread.currentThread().getName() + "准备写入数据");    
            try {    
                Thread.sleep(20);    
            } catch (InterruptedException e) {    
                e.printStackTrace();    
            }    
            this.data = data;    
            System.out.println(Thread.currentThread().getName() + "写入" + this.data);    
        } finally {    
            rwl.writeLock().unlock();// 释放写锁    
        }    
    }       
  
    public void get() {    
        rwl.readLock().lock();// 取到读锁    
        try {    
            System.out.println(Thread.currentThread().getName() + "准备读取数据");    
            try {    
                Thread.sleep(20);    
            } catch (InterruptedException e) {    
                e.printStackTrace();    
            }    
            System.out.println(Thread.currentThread().getName() + "读取" + this.data);    
        } finally {    
            rwl.readLock().unlock();// 释放读锁    
        }    
    }    
}    

 运行结果

Thread-W1准备写入数据  

Thread-W1写入9  
Thread-W1准备写入数据  
Thread-W1写入24  
Thread-W1准备写入数据  
Thread-W1写入12  
Thread-W0准备写入数据  
Thread-W0写入22  
Thread-W0准备写入数据  
Thread-W0写入15  
Thread-W0准备写入数据  
Thread-W0写入6  
Thread-W0准备写入数据  
Thread-W0写入13  
Thread-W0准备写入数据  
Thread-W0写入0  
Thread-W2准备写入数据  
Thread-W2写入23  
Thread-W2准备写入数据  
Thread-W2写入24  
Thread-W2准备写入数据  
Thread-W2写入24  
Thread-W2准备写入数据  
Thread-W2写入17  
Thread-W2准备写入数据  
Thread-W2写入11  
Thread-R2准备读取数据  
Thread-R1准备读取数据  
Thread-R0准备读取数据  
Thread-R0读取11  
Thread-R1读取11  
Thread-R2读取11  
Thread-W1准备写入数据  
Thread-W1写入18  
Thread-W1准备写入数据  
Thread-W1写入1  
Thread-R0准备读取数据  
Thread-R2准备读取数据  
Thread-R1准备读取数据  
Thread-R2读取1  
Thread-R2准备读取数据  
Thread-R1读取1  
Thread-R0读取1  
Thread-R1准备读取数据  
Thread-R0准备读取数据  
Thread-R0读取1  
Thread-R2读取1  
Thread-R2准备读取数据  
Thread-R1读取1  
Thread-R0准备读取数据  
Thread-R1准备读取数据  
Thread-R0读取1  
Thread-R2读取1  
Thread-R1读取1  
Thread-R0准备读取数据  
Thread-R1准备读取数据  
Thread-R2准备读取数据  
Thread-R1读取1  
Thread-R2读取1  
Thread-R0读取1 

与互斥锁定相比,读-写锁定允许对共享数据进行更高级别的并发访问。虽然一次只有一个线程(writer 线程)可以修改共享数据,但在许多情况下,任何数量的线程可以同时读取共享数据(reader 线程)

 

从理论上讲,与互斥锁定相比,使用读-写锁定所允许的并发性增强将带来更大的性能提高。

在实践中,只有在多处理器上并且只在访问模式适用于共享数据时,才能完全实现并发性增强。——例如,某个最初用数据填充并且之后不经常对其进行修改的 collection,因为经常对其进行搜索(比如搜索某种目录),所以这样的 collection 是使用读-写锁定的理想候选者。

转载于:https://my.oschina.net/runningwork/blog/919445

你可能感兴趣的文章
PHP - 如何打印函数调用树
查看>>
js闭包
查看>>
Swift笔记2
查看>>
寒假。3.3.G - Common Child (最大公共子序)
查看>>
052(四十二)
查看>>
设计模式学习笔记--原型模式
查看>>
.Net 通过MySQLDriverCS操作MySQL
查看>>
JS Cookie
查看>>
ubuntu Unable to locate package sysv-rc-conf
查看>>
http讲解
查看>>
测试常用脚本
查看>>
笔记:认识.NET平台
查看>>
简单的jdk代理与cglib代理Demo
查看>>
数据挖掘概念与技术笔记
查看>>
获取时间和日期
查看>>
cocos2d中CCAnimation的使用(cocos2d 1.0以上版本)
查看>>
MySQL 5.6查看数据库的大小
查看>>
android addCategory()等说明
查看>>
django信号
查看>>
java基础之反射---重要
查看>>