您的位置:首页 > 移动开发 > Android开发

浅析多线程同步

2016-07-20 16:15 633 查看

问题背景

在android开发中,我们会启一个子线程去服务器获取下发数据,这个操作一般都是比较耗时的。所以对于时效性不强的数据,为了避免每次请求服务我们一般会做缓存处理。常见的缓存策略是在本地和内存中各缓存一份,获取数据时优先从内存中获取,内存中没有则从本地获取,都没有才会去访问服务器重新获取。这种方式保证了高效的性能,但是操作内存缓存时也可能导致常见的错误:线程干扰内存一致性 的问题。

什么时候要考虑多线程同步问题

内存缓存我们一般是作为共享资源使用,但当多线程同时操作共享资源时就要考虑同步问题了。

举个例子:

1. 线程A获取缓存对象

2. 线程B获取缓存对象对缓存对象进行更新

3. 线程A对缓存对象进行操作

当A线程对缓存对象进行操作时,由于不具有原子性,其他线程会对缓存对象进行操作就产生了干扰,线程操作不可见性。这样就容易出一些bug并且隐蔽性很高。所以在多线程情况下除了读读以外,读写、写读、写写之外都要进行同步操作,保证线程安全性。

如何解决同步问题

我们不仅希望防止某个线程正在使用对象状态而另一个线程在同时修改该状态,而且还希望确保当一个线程修改了对象状态后,其他线程能够看到该变化。而线程的同步恰恰也能够实现这一点:内置锁可以用于确保某个线程以一种可预测的方式来查看另一个线程的执行结果。为了确保所有的线程都能看到共享变量的最新值,可以在所有执行读操作或写操作的线程上加上同一把锁。

使用同步锁注意点

如果采用同步方法,则对象锁即为方法所在的对象,如果是静态方法,对象锁即指方法所在的类对象(唯一)。

使用synchronized(obj)同步语句块,可以获取指定对象上的对象级别锁。obj为对象的引用,如果获取了obj对象上的对象级别锁,在并发访问obj对象时时,便会在其synchronized代码处阻塞等待,直到获取到该obj对象的对象级别锁。当obj为this时,便是获取当前对象的对象级别锁。

访问同一个类的不同实例对象中的同步代码块,不存在阻塞等待获取对象锁的问题,因为它们获取的是各自实例的对象级别锁,相互之间没有影响。

持有一个对象级别锁不会阻止该线程被交换出来,也不会阻塞其他线程访问同一示例对象中的非synchronized代码。当一个线程A持有一个对象级别锁(即进入了synchronized修饰的代码块或方法中)时,线程也有可能被交换出去,此时线程B有可能获取执行该对象中代码的时间,但它只能执行非同步代码(没有用synchronized修饰),当执行到同步代码时,便会被阻塞,此时可能线程规划器又让A线程运行,A线程继续持有对象级别锁,当A线程退出同步代码时(即释放了对象级别锁),如果B线程此时再运行,便会获得该对象级别锁,从而执行synchronized中的代码。

持有对象级别锁的线程会让其他线程阻塞在所有的synchronized代码外。例如,在一个类中有三个synchronized方法a,b,c,当线程A正在执行一个实例对象M中的方法a时,它便获得了该对象级别锁,那么其他的线程在执行同一实例对象(即对象M)中的代码时,便会在所有的synchronized方法处阻塞,即在方法a,b,c处都要被阻塞,等线程A释放掉对象级别锁时,其他的线程才可以去执行方法a,b或者c中的代码,从而获得该对象级别锁。

尽量避免在同步方法或同步块中获取其他对象的锁,容易发生死锁。

使用原则是尽量让同步块小。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息