1、synchronized定义
Java语言的关键字,可用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这个段代码。当两个并发线程访问同一个对象object中的这个加锁同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。然而,当一个线程访问object的一个加锁代码块时,另一个线程仍然可以访问该object中的非加锁代码块。
注意:synchronized无法修饰单个对象,synchronized修饰的应该是对象+方法或者对象+代码块的组合。
对象+方法:直接修饰方法即可;这种方式中没有体现出对象,但其实该类的实例就是被锁的对象了。
public synchronized void A(){ s("A"); }
对象+代码块:这种方式就比较明显了,this就是指该类的实例,{}中的内容则是代码块。
public void A(){ synchronized (this) { s("A"); } }
上面两个例子是等价的。
2、打个比方来理解
synchronized其实就是一个游戏
游戏场景:一个大房子(对象),里面有多个房间(方法),有些是锁着的,有些是没锁的
游戏人物:管理员(JVM),玩家(线程)
游戏规则:
(1)没有上锁的房间可以随时使用,也可以多个玩家同时使用
(2)上了锁的房间只限一个玩家使用,而且只有一把钥匙可以打开
(3)钥匙可以跟管理员拿,若同时有多个玩家想拿同个钥匙,管理员会随机给其中一个玩家
(4)玩家用完房间后必须把钥匙归还管理员(即使想再次使用,也得先还管理员,再跟管理员要;如果其他玩家也要这个钥匙,则会随机给其中一个)
3、Android用到的地方
首先从定义就可以知道,synchronized只有在多线程的场景下才有存在的意义。
那么Android中用到多线程的地方,举个常见的例子,就是GridView异步加载本地图片。
由于加载图片挺耗时的,所以在同个线程中把多张图片一次加载完是很不明智的,况且本地图片可是很多的。
所以这时我们会考虑到使用线程池来加载图片。
那么线程多了自然也会面临一个问题:同个图片可能会被多个线程所加载,会产生什么问题?
事实上,这也是必然会出现的问题;比如你快速向上滑动图片列表(在图片非常多的情况下),然后又迅速向下滑动,这时如果之前正在加载的图片还没加载完,就会有新的线程来再加载那张图片;倒不至于报错,只是同张图片可能会被多次加载,界面上会有图片一闪的现象,而且挺浪费内存的。
而这时候,如果用上synchronized关键字,就可以轻松解决了~~至于怎么设计代码,这里就不多提,注意锁住的对象必须是同一个就行。
再次提醒:锁住不同对象没有任何意义,类似下面这样的代码都是无意义的!
public class Test implements Runnable{ @Override public void run() { synchronized (this) { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName()); } } } public static void main(String...arg){ new Thread(new Test(),"A").start(); new Thread(new Test(),"B").start(); } }
应该写成这样:
Test test = new Test(); new Thread(test,"A").start(); new Thread(test,"B").start();