Java多线程 之 访问共享资源synchronized、lock(七)
2016-06-12 16:07
561 查看
上一篇博文引出了资源竞争,并使用例子展示了资源竞争产生的结果(错误)。这篇博文给出解决方法。
在java中,所有对象都自动含有单一的锁。也就是说,当一个任务调用某个对象上被synchronized标识的f()方法时,其他任务不能再调用该对象上被synchronized标识的g()方法,但是这个任务却可以在f()中嵌套调用g()方法。其实现原理是这样的:一个任务可以多次获得对象的锁(《Thinking in java》中文第四版677页的原话),即如果一个对象被解锁(锁被完全释放),其计数变为0;当一个任务第一次给对象加锁的时候,计数器变为1,每当这个相同的任务在这个对象上获得锁时(嵌套调用),计数就会递增;只有首先获得锁的任务才能继续获得锁(因为,这个计数不为0了);每当任务离开一个synchronized方法,计数递减,当计数为0时,锁被完全释放,其他任务可以获得锁。JVM负责跟踪对象被加锁的次数。
在java中,针对每个类也有一个锁(作为类的class对象的一部分)。这样,synchronized static方法可以在类的范围内防止对static数据的并发访问。也就是说,一个任务调用了被synchronized static标识的f()方法,则其他任务不能再调用被synchronized static标识的g()方法。
每个访问临界资源的方法都必须被同步。
下面是使用synchronized关键字解决上篇博文中并发错误的例子:
这个即使调用了yield方法,也不会产生任何并发错误。
看个例子:
注意使用lock方式来同步,最好使用上面的写法来写,特别是return语句的位置。return语句必须在try子句中出现,以确保unlock不会过早放生,从而将数据暴露给了第二个任务。
lock方式还可以使用trylock方式,如果没有获得锁,不会一直阻塞,而是直接返回。
还可以给trylock传递一个时间,表示在这个时间段内如果没有获取锁会直接返回。
1. synchronized
解决线程冲突的方案基本上都是:序列化访问共享资源。即当多个线程对共享资源同时访问时,对共享资源加锁访问。在Java中提供了synchronized关键字。在java中,所有对象都自动含有单一的锁。也就是说,当一个任务调用某个对象上被synchronized标识的f()方法时,其他任务不能再调用该对象上被synchronized标识的g()方法,但是这个任务却可以在f()中嵌套调用g()方法。其实现原理是这样的:一个任务可以多次获得对象的锁(《Thinking in java》中文第四版677页的原话),即如果一个对象被解锁(锁被完全释放),其计数变为0;当一个任务第一次给对象加锁的时候,计数器变为1,每当这个相同的任务在这个对象上获得锁时(嵌套调用),计数就会递增;只有首先获得锁的任务才能继续获得锁(因为,这个计数不为0了);每当任务离开一个synchronized方法,计数递减,当计数为0时,锁被完全释放,其他任务可以获得锁。JVM负责跟踪对象被加锁的次数。
在java中,针对每个类也有一个锁(作为类的class对象的一部分)。这样,synchronized static方法可以在类的范围内防止对static数据的并发访问。也就是说,一个任务调用了被synchronized static标识的f()方法,则其他任务不能再调用被synchronized static标识的g()方法。
每个访问临界资源的方法都必须被同步。
下面是使用synchronized关键字解决上篇博文中并发错误的例子:
package org.fan.learn.thread.share; /** * Created by fan on 2016/6/12. */ public class SynchronizedEvenGenerator extends IntGenerator { private int evenValue = 0; @Override public synchronized int next() { ++evenValue; Thread.yield(); ++evenValue; return evenValue; } public static void main(String[] args) { EvenChecker.test(new SynchronizedEvenGenerator()); } }
这个即使调用了yield方法,也不会产生任何并发错误。
2.lock
lock看上去很直接,就是加锁。《Thinking in java》说,lock与内建的锁形式相比,代码缺乏优越性,但是对某些具体问题,lock更加灵活。看个例子:
package org.fan.learn.thread.share; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Created by fan on 2016/6/12. */ public class LockEvenGenerator extends IntGenerator { private int evenValue = 0; Lock lock = new ReentrantLock(); @Override public int next() { lock.lock(); try { ++evenValue; Thread.yield(); ++evenValue; return evenValue; } finally { lock.unlock(); } } public static void main(String[] args) { EvenChecker.test(new LockEvenGenerator()); } }
注意使用lock方式来同步,最好使用上面的写法来写,特别是return语句的位置。return语句必须在try子句中出现,以确保unlock不会过早放生,从而将数据暴露给了第二个任务。
lock方式还可以使用trylock方式,如果没有获得锁,不会一直阻塞,而是直接返回。
还可以给trylock传递一个时间,表示在这个时间段内如果没有获取锁会直接返回。
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- Python3写爬虫(四)多线程实现数据爬取
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序