作者punk86862001 (赵MAN)
看板AndroidDev
标题[分享] 利用 Synchronized 宣告来保护物件的存取
时间Tue Jan 21 23:09:12 2014
有图网志有排版好读版
http://goo.gl/cA6kBE
------
前言
我们都知道 Android 常会利用 multi-thread 来做多工的处理
但是如果同时有两个以上的 Thread 对同一个物件做资料的存取甚至是更改
没有处理好的话会导致意想不到的结果发生.
举个例子来说,假设有一个地方邮局的邮箱如下图所示
这个邮局有两个邮差 A 跟 B 来负责送信
并且有一个人会负责记录剩余的邮件数量
我们先假设这个记录的人叫做 Kenji 好了
有一天邮差 A 到了邮局,Kenji 告诉他目前待处理的邮件有 100 封
接着 A 就开始从邮箱寻找他要负责处理的信件
然後邮差 B 也来了,Kenji 一样告诉他还剩下 100 封邮件
一样邮差 B 也去邮箱找信件
这时邮差 A 拿了他现在要处理的 20 封邮件离开,
告诉 Kenji 现在剩下 80 封
过了不久邮差 B 也拿了要处理的 30 封邮件
但他并不知道邮差 A 已经拿走了一些邮件,於是告诉 Kenji 现在剩下 70 封
Kenji 记录下现在的邮件数量:70,但很明显地我们知道实际上只剩下 50 封
这就是 multi-thread 同时对一个物件存取有可能产生错误的情境
为了避免这样的情形发生
以下我们要来介绍 synchronized 宣告的使用
Synchronized 的使用方法
1. Synchronized Method
synchronized public void method1() {
...
}
此种方法对於只有一个 instance 的 class 来说
能够确保在同一个时间只有一个 Thread 正在执行此 Method
但是如果 new 了很多 instance 出来就没有办法保证了
2. Synchronized Static Method
synchronized public static void method2() {
...
}
为上一种方法的加强版,就算有很多 instance 还是可以确保
同一时间只有一个 Thread 正在执行此 Method
3. Synchronized(this)
public void method3() {
...
synchronized(this) {
...
}
...
}
此种方法跟第一种方式类似
差别只是写在 synchronized(this) 之前的程式码可以同时被执行
synchronized(this) 之後的程式码同时只有一个 Thread 可执行
我知道这样讲完还是有点抽象
让我们来看看下面的例子!
Synchronized 的实际例子
Synchronized.class
首先我们先建立一个 class
里面有四个 Method
1. method0(): 普通没有做 synchronized 的 method
2. method1(): 实作上面的 Synchronized Method
3. method2(): 实作上面的 Synchronized Static Method
4. method3(): 实作上面的 Synchronized(this)
做的事情很简单,只是每过一秒吐一个 Log 出来
并且显示现在正在执行的 Thread
public class Synchronized {
final static private String TAG = "Kenji";
public static void method0() {
int i = 0;
while (i++ < 3) {
Log.d(TAG, Thread.currentThread().getName() + ":" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
synchronized public void method1() {
method0();
}
synchronized public static void method2() {
method0();
}
public void method3() {
Log.d(TAG, "Start method3");
synchronized (this) {
method0();
}
Log.d(TAG, "End method3");
}
}
MainActivity.class
接着我们在 MainActivity 新增两个 Thread
名字分别为 A 跟 B
接着观察执行不同的 method 时 Log 的变化
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Synchronized mSynchronized = new Synchronized();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
mSynchronized.method0();
// mSynchronized.method1();
// new Synchronized().method1();
// new Synchronized().method2();
// mSynchronized.method1();
}
},"A");
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
mSynchronized.method0();
// mSynchronized.method1();
// new Synchronized().method1();
// new Synchronized().method2();
// mSynchronized.method3();
}
}, "B");
Log.d("Kenji", "Start Thread!");
thread1.start();
thread2.start();
}
运行的结果如下:
1. 执行正常的 method 两个 thread A, B 会交错出现
thread1: mSynchronized.method0();
thread2: mSynchronized.method0();
--------------------------------------------
Start Thread!
A:1
B:1
B:2
A:2
A:3
B:3
2. 执行 Synchronized method 会先等其中一个 thread 执行完
才会再度执行相同的程式码区块
thread1: mSynchronized.method1();
thread2: mSynchronized.method1();
--------------------------------------------
Start Thread!
A:1
A:2
A:3
B:1
B:2
B:3
3. 但是如果有超过一个物件的话,程式码会同时执行
thread1: new Synchronized().method1();
thread2: new Synchronized().method1();
--------------------------------------------
Start Thread!
B:1
A:1
A:2
B:2
A:3
B:3
4. 如果采用 Synchronized Static Method 的话
就可以解决上面同时执行的问题
thread1: new Synchronized().method2();
thread2: new Synchronized().method2();
--------------------------------------------
Start Thread!
A:1
A:2
A:3
B:1
B:2
B:3
5. 写在 synchronized(this) 之前的程式码可以先同时执行
thread1: mSynchronized.method1();
thread2: mSynchronized.method3();
--------------------------------------------
Start Thread!
A:1
Start method3
A:2
A:3
B:1
B:2
B:3
End method3
总结
Synchronized 宣告其实是在 multi-thread 中蛮值得学习的一些观念
以下是我对 Synchronized 的一些理解:
1. 只要有 thread 存取了某个 class 中的 synchronized method,
那麽其他的 thread 也不能存取整个 class 中所有的 synchronized method
也就是在同一个时间内只会有一个 method 存取 synchronized 区块的程式码
2. 相反的,当一个 thread 正在执行 synchronized 区块时
其他 thread 仍然可以执行 非synchronized 区块的程式码
希望对大家有帮助罗!谢谢!
参考资料:
1. java同步机制:synchronized
http://blog.csdn.net/cjjky/article/details/7353390
2. 执行绪与 synchronized 同步函式的应用
http://dazi2012.blogspot.tw/2013/04/synchronized.html
3. [Java] Synchronized 心得
http://www.jackforfun.com/2007/07/java-synchronized.html
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 36.224.182.204
1F:推 Y78:推 01/21 23:15
2F:推 asadman1523:辛苦你了!还要排版好累 01/21 23:23
3F:→ punk86862001:排版真的有点麻烦QQ 01/21 23:25
4F:推 ian90911:推 01/22 18:37
5F:推 pttnews:谢谢原P ,你是个好人 01/23 15:15
6F:推 Killercat:最好别太依赖这种semaphore类型的保护 效能会很糟 01/23 18:24
7F:→ Killercat:应该先往thread safe或者thread local相容的方向着手 01/23 18:25
8F:→ Killercat:用synchornized是最後不得已的方法 而且目前来讲 01/23 18:25
9F:→ Killercat:看到的95%的synchornized都是可避免的居多 01/23 18:25
10F:→ Killercat:尤其是Thread local,这是个该善加利用的好东西 01/23 18:25
11F:→ asadman1523:请问那如果是银行那种存提款系统,该如何避免呢? 01/24 23:58
12F:推 youweit:学习了!很清楚地解释了synchronized 好文 01/27 19:53
13F:推 chadcoco1222:push 01/30 00:56
14F:推 jealdedani:推 02/01 21:34