android 并发同步,常用的锁


android 正常业务逻辑中,一般并发用到的地方不多,不像服务器那样,但并发也是存在的,如果没处理好,会引起线程安全的问题。为了解决这些问题,我们会用些线程安全的数据容器,或者使用并发工具类,再或者自己对方法进行些加锁操作等,这里简单介绍几种常用的方法。

synchronized 是关键字,它是作用在JVM上的,我们一般把它用于方法只上,也可以用在方法内,举个栗子

public class TestDemo {

    public synchronized static void test1(){

    }

    private synchronized void test2(){

    }

    Object object = new Object();
    private void test3(){
        synchronized (object){

        }
    }

    static Object object2 = new Object();
    private void test4(){
        synchronized (object2){

        }
    }

    private void test5(){
        synchronized (TestDemo.class){

        }
    }

}


test1() 方法是静态的,所以这个方法中是同步的,因为 TestDemo.test1() 可以直接调用。
test2() 这个方法非静态,它是针对对象的,此时,如果创建一个对象 TestDemo testDemo = new TestDemo(),然后开启了多条线程执行 test2() 方法,此时test2() 里面是线程安全的;但如果开启了多条线程,在每条线程里执行 new TestDemo().test2() 方法,则它不是线程安全的,因为此时针对的是对象而非是类。
test3() 此时 synchronized 关键字是在方法里,它锁定的是类的非静态成员变量 object ,此时 test3() 和 test2() 一样,是否安全根据调用者来判断。
test4() 它和test3()的区别就是此时锁的对象 object2 是静态成员变量,所有的同类型对象都共享静态值,所以此时锁定的是类,也就是说开启多条线程,每个线程都创建一个对象执行 test4() 方法,该方法仍然是线程安全的。
test5() 它锁的对象是 TestDemo.class ,我们知道java中同类型对象可以创建很多个,但他们共用同一个 .class ,所以它和 test4() 是一样的,线程安全。


lock 是类级别的锁,它可以锁定任何一段代码,它的作用和 synchronized 类似;它有个锁和释放锁的概念,记得一定要释放,否则可能造成死锁,它锁定的是针对 lock 对象,共用lock的代码,比如 

    Lock lock = new ReentrantLock();
    private void test6(int i){
        try {
            lock.lock();
//            ...
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

此时它的功能和 test3() 一样。假如把 lock 变为静态成员变量,那么它的功能就和 test4() 一样了。


以上两种就是常用的锁,还有一个 ReadWriteLock 是针对上面做了优化,说是优化,其实是从业务方法进行的。比如说数据的读和写,我们一般都是互斥的,防止数据错乱,但假如有些需求,读的话仅仅是展示而非有其他的操作,那么同时去读取是不是更节省时间,假如读数据需要50毫秒,有三条线程去读,如果使用 sychronized 或 Lock ,由于它们是互斥的,所以会耗时150毫秒,同时去读的话就可以提高效率;那么可能有人会说,读的方法不用同步锁就可以,那会引起一开始说的问题,读和写互斥,如果不加同步,正在读的时候,忽然有数据写入,那就糟了,所以还是要加锁,此时 ReadWriteLock 就登场了。

   ReadWriteLock rwl = new ReentrantReadWriteLock();
    private void set(){
        try {
            rwl.writeLock().lock();
            System.out.println("write start  " + Thread.currentThread().getName());
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("write end  " + Thread.currentThread().getName());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rwl.writeLock().unlock();
        }
    }

    private void get(){
        try {
            rwl.readLock().lock();
            System.out.println("read start  " + Thread.currentThread().getName());
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("read end  " + Thread.currentThread().getName());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rwl.readLock().unlock();
        }
    }

    public static void main(String[] args){
        final TestDemo testDemo = new TestDemo();
        for (int i = 0; i < 5; i++){
            final int index = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    if (index > 3){
                        testDemo.set();
                    } else {
                        testDemo.get();
                    }

                }
            }).start();
        }
    }

打印出来的值为

read start  Thread-0
read end  Thread-0
write start  Thread-4
write end  Thread-4
read start  Thread-1
read start  Thread-3
read start  Thread-2
read end  Thread-1
read end  Thread-3
read end  Thread-2


对于线程安全,如何选择使用的工具,这个根据我们的业务作出选择。


 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Android开发中,正确处理多线程并发问题非常重要。以下是一些处理多线程并发问题的最佳实践: 1. 使用线程安全的数据结构:使用线程安全的数据结构可以在多个线程之间共享数据,而不会导致数据损坏或不一致。例如,可以使用ConcurrentHashMap代替HashMap,在多个线程之间进行读写操作。 2. 使用同步代码块:同步代码块可以确保在一个线程修改共享数据时,其他线程不会同时修改该数据。例如,可以使用synchronized关键字同步代码块来防止多个线程同时访问代码块中的共享变量。 3. 使用:使用可以确保在一个线程修改共享数据时,其他线程不会同时修改该数据。例如,可以使用ReentrantLock类来实现。 4. 避免死:死是一种情况,在这种情况下,两个或多个线程互相等待对方释放资源,导致程序无法继续执行。为避免死,应该避免使用多个,或者确保获取的顺序是一致的。 5. 使用线程池:线程池可以管理和复用线程,避免了线程创建和销毁的开销。在Android开发中,可以使用ThreadPoolExecutor或者AsyncTask等类来实现线程池。 6. 使用Handler:Handler是Android中处理多线程的机制之一。使用Handler可以在主线程和其他线程之间进行通信,从而避免了一些并发问题。 最后,要时刻注意多线程并发的风险,确保代码中没有竞态条件等问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值