《深入解析Android 5.0系统》——第6章,第6.3节Android Java层的同步机制
本节书摘来自异步社区《深入解析Android 5.0系统》一书中的第6章,第6.3节Android Java层的同步机制,作者 刘超,更多章节内容可以访问云栖社区“异步社区”公众号查看
6.3 Android Java层的同步机制
深入解析Android 5.0系统
Java语言和C/C++语言不一样,Java语言中提供了同步关键字synchronized来支持线程间的同步操作。
6.3.1 同步关键字synchronized
synchronized关键字最常见的用法是保护一段代码,如下所示:
class Foo implements Runnable private String mLock; public void lockedMethod() { ...... synchronized(**mL**ock) { ...... } ...... synchronized还可以用在类的方法前面,如下所示: class FooA implements Runnable public **synchronized******void lockedMethod() { ..... } public **synchronized******void unlockedockedMethod() { ..... } synchronize的第三种用法是修饰一个静态方法,如下所示: class FooB implements Runnable public **synchronized**** static **void lockedMethod() { ...... } }
这3种使用方法的含义有很大的区别。首先要理解,synchronize锁住的是对象,而不是一段代码。在Java中,每个对象都可以是一把锁,这意味着每个Java对象都有类似native层的Mutex对象的功能。而synchronize关键字则和native层的Autolock类似,起到的是自动加锁和解锁的作用。
对于上面介绍的第一种使用方法:使用synchronize保护一段代码,假设类Foo有两个实例对象foo1和foo2,如果在两个线程中分别调用fool.lockedMethod()方法,在同一时间,只有一个线程能执行被synchronize括起来的代码,也就起到了互斥作用。但是如果一个线程调用的是fool.lockedMethod(),另一个线程调用的是foo2.lockedMethod(),这样是不会互斥的。原因是对于前者,上锁的对象是foo1.mLock,后者上锁的对象是foo2.mLock,它们其实是两把完全不同的锁。
synchronize关键字的第二种用法:“用synchronize修饰一个非静态方法”,语法上等价于下面这种写法:
synchronized (this) { ...... }
它表明上锁的对象是类的实例对象本身,而第一种用法中上锁的对象只是实例对象的成员变量,锁的范围扩大了。这样导致的结果是两个线程对于同一个实例对象的任何访问都将会互斥。具体而言:对于方法二中的例子,假定类FooA有一个实例对象foo_a,在两个线程中都访问foo_a.lockMethod()一定会互斥,一个线程访问foo_a.lockMethod(),另一个线程访问不同的函数foo_a.unlockedMethod()同样会互斥。当然如果是两个类实例对象foo_a和foo_b,分别调用它们的函数都不会互斥。
synchronize关键字的第三种用法:“用synchronize修饰一个静态的方法”,语法上等价于下面的写法:
synchronized (FooB.class) { ...... }
它表明上锁的对象是类本身,这样锁的作用范围进一步扩大了。因此,导致的结果是一旦有一个线程调用的FooB类的任何实例对象的lockedMethod()方法,另一个线程对于FooB类的任何对象的lockedMethod()方法都将会将产生互斥。这种用法导致的结果是最严厉的。
从上面的介绍可以看到synchronized并不像表面看上去的那么简单,在一个函数内部使用synchronized关键字,有可能会导致对整个类的访问都被锁定(当然这样会显得比较晦涩)。可能也是这个原因,Java在语法上创造了用synchronize来修饰一个方法,这样至少看上去会更明显些。
6.3.2 Object类在同步中的作用
synchronized关键字只能提供互斥访问的同步机制,如果需要完成一些更复杂的同步操作,就需要Object类来配合完成。Object类是所有Java对象的基类,在Object类中提供了一些与同步相关的方法,如下所示:
public final native void notify(); public final native void notifyAll(); public final void wait(); public final void wait(long millis); ublic final native void wait(long millis, int nanos);
同步相关的接口分为notify和wait两种类型,notify类型的函数用来唤醒挂起的线程,wait类型的函数用来挂起调用者所在的线程。
要注意的是,这些方法并不是随时随地都可以调用的,对它们的调用必须是在本线程取得对“Object对象”的控制权以后才能进行。更准确地说,应该在synchronized的作用域内调用。这可能有点不太好理解,下面来看一个使用Object对象做同步控制的例子,代码如下:
public class ObjectSync implements Runnable { private final static String Tag = "ObjectSync"; private String name; private Object lock; private ObjectSync(String name, Object lock) { this.name = name; this.lock = lock; @Override public void run() { int count = 10; while (count 0) { synchronized (lock) { Log.i(Tag, name); lock.notify(); try { if(--count 0) { lock.wait(); } catch (InterruptedException e) { Log.e(Tag, e.getMessage()); public static void output() { Object lock = new Object(); Thread threadA = new Thread(new ObjectSync("A", lock)); Thread threadB = new Thread(new ObjectSync("B", lock)); threadA.start(); threadB.start(); try { threadA.join(); threadB.join(); } catch (InterruptedException e) { e.printStackTrace(); Log.i(Tag, "thread A B are exited."); }
上面的例子中notify()和wait()都是在对lock对象操作,因此,它们必须在synchronized(lock)的代码块内调用,否则运行时会报异常。当然也可以把对notfiy()和wait()的调用放到一个方法内,然后在synchronized块内调用这个方法。
这个例子的目的是在两个线程中分别输出文字“A”和“B”,但是输出结果要是交替的,也是就“ABABAB”的形式。例子中output()方法是入口,它创建了一个Object对象lock作为锁,然后又创建了两个线程。两个线程用的是同一个线程类,但是用参数加以区别,并通过参数把lock对象传给了两个线程中,这样两个线程中将使用同一个对象锁。在线程的运行方法run()中,首先用synchronized关键字形成了一个临界区,进入临界区后,先打印输出,然后调用notify()方法,恢复对方线程的运行,再调用wait()使自己挂起,接着对方线程运行,打印结果后,再重复上面的过程。这样两个线程就交替运行起来了。
Android | 通过WindowInsetsController设置系统栏颜色、Behavior、显示隐藏等 两种方式可以引入`WindowInsetsController`,一种直接通过`API`的`val controller = window.decorView.windowInsetsController`获取,注意该方法在`API30`及以上才有
android体系课-系统启动流程-之zygote进程启动过程源码分析 笔者刚开始学习Android的时候也和大部分同学一样,只会使用一些应用层面的知识,对于一些比较常见的开源框架如 mark RxJava /mark , mark OkHttp /mark , mark Retrofit /mark ,以及后来谷歌推出的 mark 协程 /mark 等,都只在使用层面,对于他们 mark 内部原理 /mark ,基本没有去了解觉得够用就可以了,又比如Activity,Service等四大组件的使用原理,系统开机过程,Launcher启动过程等知之甚少,知其然而不知其所以然,结果就是出现某些问题,不知道从哪里找原因,只能依赖万能的百度,但是百度看多了,你会发现自己
android体系课-系统启动流程-之SystemServer启动过程源码分析 笔者刚开始学习Android的时候也和大部分同学一样,只会使用一些应用层面的知识,对于一些比较常见的开源框架如 mark RxJava /mark , mark OkHttp /mark , mark Retrofit /mark ,以及后来谷歌推出的 mark 协程 /mark 等,都只在使用层面,对于他们 mark 内部原理 /mark ,基本没有去了解觉得够用就可以了,又比如Activity,Service等四大组件的使用原理,系统开机过程,Launcher启动过程等知之甚少,知其然而不知其所以然,结果就是出现某些问题,不知道从哪里找原因,只能依赖万能的百度,但是百度看多了,你会发现自己
异步社区 异步社区(www.epubit.com)是人民邮电出版社旗下IT专业图书旗舰社区,也是国内领先的IT专业图书社区,致力于优质学习内容的出版和分享,实现了纸书电子书的同步上架,于2015年8月上线运营。公众号【异步图书】,每日赠送异步新书。
相关文章
- [Android Pro] git 打标签、推送tag到托管服务器、验证是否成功
- [Android Pro] java.lang.IllegalStateException: Fragment(XXFragment) not attached to Activity异常
- JAVA Eclipse开发Android如何让屏幕保持为竖直或水平状态
- Android中StateListDrawable的种类(状态的种类)
- Java 蓝桥杯 算法训练 字符串的展开 (JAVA语言实现)
- android 之ViewStub
- Android UI(二)DridView的菜单
- 1、Android Studio集成极光推送(Jpush) 报错 java.lang.UnsatisfiedLinkError: cn.jpush.android.service.PushProtoco
- 【XRefreshView】打造android万能上拉下拉刷新框架(转载)
- 《android开发艺术探索》读书笔记(十)--Android的消息机制
- Atitti.java android反编译解决方案-----虚拟机方案
- Android性能优化之捕获java crash示例解析
- Android的java本地调用
- Android 隐藏App图标
- Android Studio 4.2.2的报错提示:Could not determine java version from ‘11.0.8‘. The project uses Gradle v
- 【项目实战】接入极光推送SDK,实现从Java服务端后台推送自定义消息到Android车机端
- 【Java】java 环境配置(详细教程)
- 【Android 逆向】ELF 文件格式 ( 程序头偏移量 | 节区头偏移量 | 处理器特定标志 | ELF 文件头大小 )
- 我的Android进阶之旅------>Android编译错误java.util.zip.ZipException: duplicate entry的解决方法
- Android-Socket传输 GPRS网络
- Android下将图片载入到内存中
- java.lang.OutOfMemoryError: Java heap space
- 使用Android Studio build tensorflow/examples/android——直接用android studio即可
- Android的重复jar报错提示:java.util.zip.ZipException:duplicate entry:okio/AsyncTimeout$1.class
- Android 解决之依赖包中添加的com.android.support版本不同导致冲突的问题
- 【java】Java 中泛型的实现原理
- 【java】Java线程池实现原理及业务中的实践
- JAVA开发讲义(二)-Java程序设计之数据之谜三
- android开发,Android Studio在创建安卓虚拟设备(AVD)时,报错:Unknown Error
- Android/Linux线程死锁demo分析(九十六)