Android并发编程之如何使用ReentrantReadWriteLock替代synchronized来提高程序的效率

标签: android并发编程多线程 1450人阅读 评论(1) 收藏 举报  分类:Android并发编程(4) 

Java的synchronized关键字可以帮助我们解决多线程并发的问题,比如我们有一个公共资源,多个线程都来操作这个公共的资源,就会出现并发的问题,比如不同的线程对同一个数据同时进行读和写,肯定会使得每个线程最后拿到的都不是自己所希望拿到的值,为了解决这个问题,我们可以使用synchronized关键字加锁。

以前synchronized由于性能消耗太大,在Java SE 1.6对它进行了优化,使得synchronized锁现在有4种状态:无锁、偏向锁、轻量级锁、重量级锁,他们性能消耗是由低向高的,在没有竞争出现的时候,它是偏向锁,性能消耗非常小,基本就没什么消耗,当竞争来了并且竞争变大,它就会逐渐升级成重量级锁,此时的锁的性能开销很大,当一个线程获得锁后,其它的线程只能阻塞等待。

这就是关键,当线程竞争时,synchronized会升级成重量级锁,当一个线程持有锁时,其他的线程只能阻塞等待,有些时候会给我们带来不必要的性能损耗。

我们先来看一段代码

public class Storage {    //被操作的公共数据    private int num;    //使用同步方法,锁对象是当前类对象    private synchronized void write(){        try {            //模拟一下耗时操作            TimeUnit.SECONDS.sleep(1);        } catch (InterruptedException e) {            e.printStackTrace();        }        //给num增10        num+=10;        System.out.println("Writer:num="+num);    }    //使用同步方法,锁对象是当前类对象    private synchronized void read(){        try {            //模拟一下耗时操作            TimeUnit.SECONDS.sleep(1);            System.out.println("Reader:num="+num);        } catch (InterruptedException e) {            e.printStackTrace();        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

Storage类中有一个公共变量,write()和read()方法是来操作公共变量num的,我们使用synchronized关键在修饰方法,锁对象为当前类对象,那么当执行write()方法时,不能有线程执行read()方法,同理当执行read()方法时,也不能有线程执行write()方法。

public static class Writer implements Runnable{        private Storage storage;        public Writer(Storage storageByLock){            storage = storageByLock;        }        @Override        public void run() {            storage.write();        }    }    public static class Reader implements Runnable{        private Storage storage;        public Reader(Storage storageByLock){            storage = storageByLock;        }        @Override        public void run() {            storage.read();        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

这是写着和读者,他们实现了Runnable接口,在run方法里调用了Storage类的write()方法和read()方法。

public static void main(String[] args) {        Storage storage = new Storage();        List threads = new ArrayList();        for(int i=0 ; i<5 ; i++){            Thread t = new Thread(new Writer(storage));            threads.add(t);        }        for(int i=0 ; i<5; i++){            Thread t = new Thread(new Reader(storage));            threads.add(t);        }        long startTime = System.currentTimeMillis();        for(Thread t : threads){            t.start();        }        for(Thread t : threads){            try {                t.join();            } catch (InterruptedException e) {                e.printStackTrace();            }        }        long endTime = System.currentTimeMillis();        System.out.println("time="+(endTime-startTime));    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

这是我们的main方法,首先我们new了一个Storage对象,作为公共的资源,我们创建了5个写者和5个读者,他们分别都是一个线程,我们来运行一下,看看他们是如何执行的 
Android并发编程之如何使用ReentrantReadWriteLock替代synchronized来提高程序的效率_第1张图片 
我们看到他们是顺序执行的,当一个线程正在执行时,其他的线程是阻塞的,synchronized关键字为我们解决了并发引起的线程安全问题。

但是,我们想象这样一种情况,比如写着很少,读者很多,而读者只是想读出数据,并没有对数据进行修改,那么在读者很多的情况下,它还是得按照一个一个的顺序来执行,这样的效率会很慢,我们来模拟一下这种情况

for(int i=0 ; i<1; i++){            Thread t = new Thread(new Writer(storage));            threads.add(t);        }        for(int i=0 ; i<10; i++){            Thread t = new Thread(new Reader(storage));            threads.add(t);        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

我们现在只需要一个写着,但是我们有10个读者,我们来看一看运行的情况 
Android并发编程之如何使用ReentrantReadWriteLock替代synchronized来提高程序的效率_第2张图片 
我们看到读者们只是读出数据,并没有改变数据,他们读的值都是一样的,最后程序运行了11秒多才结束。

所以,上面那个情况是有问题的,我们应该需要这样一种机制,就是当有写者正在写的时候,他是独占资源的,其他无论读者还是写者只能阻塞等待;当没有写者正在写的时候,读者们是可以并行读到数据的,这样当写着很少,读者很多的时候,读者们几乎可以同时完成读的操作,这样就大大提升了程序的运行效率。

Java给我们提供了ReentrantReadWriteLock可以解决上面的问题,我们将Storage类中的write()方法和read()方法使用ReentrantReadWriteLock来进行加锁

public class Storage {    //被操作的公共数据    private int num;    //Lock锁    private ReadWriteLock lock = new ReentrantReadWriteLock();    private void write(){        //获取到写者锁        //当线程获取到写着锁时,其他线程不可以再获得写者锁和读者锁        //它就相当于synchronized锁住的方法        lock.writeLock().lock();        try{            TimeUnit.SECONDS.sleep(1);            num+=10;            System.out.println("Writer:num="+num);        } catch (InterruptedException e) {            e.printStackTrace();        }finally{            //一定不要忘了在finially中释放锁            lock.writeLock().unlock();        }    }    private void read(){        //获取读者锁        //读者锁可以同时由很多个线程获得,因此可以增加效率        lock.readLock().lock();        try{            TimeUnit.SECONDS.sleep(1);            System.out.println("Reader:num="+num);        } catch (InterruptedException e) {            e.printStackTrace();        }finally{            //一定不要忘了释放锁            lock.readLock().unlock();        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

这里需要解释一下:为什么读者锁可以由多个线程同时获得? 
如果当前没有写者存在,那么线程可以持有读者锁(只要有写者存在,就不能有读者存在,只有当写者释放了写者锁之后,读者才能够获得读者锁),每当一个线程持有一个读者锁后,系统就会将读者锁的数量加一,每当一个线程释放一个读者锁后,系统会将读者锁的数量减一,只有当读者锁的数量为0时,写者才能够获得写者锁,否则他会阻塞等待所有的读者都读取完毕后,才能进行写操作!

好了,其他地方的代码不变,我们还是模拟1个写者和10个读者,我们看看运行的结果如何 
Android并发编程之如何使用ReentrantReadWriteLock替代synchronized来提高程序的效率_第3张图片 
看见没,2秒就结束了!我们几乎提高了4倍的运行效率!如果在高并发的环境下,读者千千万万个,那么提高的性能就更加的明显了!

更多相关文章

  1. Android之 UI主线程
  2. 另一个更简单的Android应用程序全屏的方法
  3. Android SDK4.0 离线安装方法
  4. Android 全屏显示的两种方法
  5. Android中设置文本颜色的三种方法
  6. Android P(api28) 不支持 http 协议解决方法
  7. 三步搞定:Vue.js调用Android原生方法
  8. android 防止键盘弹出的简单方法
  9. Android高手进阶教程(十七)之---Android中Intent传递对象的两种

随机推荐

  1. android 应用名称排序
  2. Android(安卓)获取照相机图片或本地图片
  3. Android studio 无法搜索插件
  4. Android 存储设备管理 -- Vold
  5. Android下拉刷新总结
  6. android修改MTP及MTP(PC显示名称)、PTP盘
  7. Android Studio报错Manifest merger fail
  8. 【Android自学笔记之一】个性TextView自
  9. Android 的状态栏设置图片填充或者颜色填
  10. Android Bitmap内存限制