线程同步在java中的面目
holmos-cache-core也就是holmos分布式缓存系统,配置模块已经开发完毕;缓存的配置,我尽量做到简单,只有不到5中配置节点,简单明了,支持properties和xml两种配置文件格式;而且咱做了个创新,不但支持本地配置文件,还支持http协议和ftp远程配置文件配置。而且不重启系统,更改配置,系统会及时发现,并应用新的配置,等等等等,具体什么样子,给大家一个代码截图,先睹为快。

看着标题,不是说线程同步的嘛,大家莫急,因为马上要写缓存的核心功能,单点的读取和写入,多线程那是必备的知识啊,在此写文一篇,欢迎各路豪杰畅读拍砖。
线程作为java一次执行的最基本的单位,而进程作为管理程序的基本单位,一个程序执行,至少有一个进程作为管理的依托,由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。这个也就是同步产生的缘由吧。
说起java的同步,就不得不说synchronized关键字,学同步不认识它就犹如你学屋里不认识爱因斯坦,牛顿一样;下面咱们就详细说说这个关键字,他究竟何方人物?!
synchronized关键字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。如果 再细的分类,synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。
先声明几点:
1)无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁――而且同步方法很可 能还会被其他线程的对象访问。
2)每个对象只有一个锁(lock)与之相关联。
3)实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
1、synchronized关键字的作用域
他的作用域分两种,一种是对象级别,一种是类级别;
对象级别:例如对同步非静态方法,同步非静态方法中的代码块;对于非静态方法,synchronized aMethod(){}就可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线 程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的 synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法;对于非静态方法中的代码块也是如此。
synchronized 方法控制对类成员函数的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得 该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变 量的方法均被声明为 synchronized)。
类级别:看完对象级别,类级别也就简单多了;synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。
在 Java 中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为 synchronized ,以控制其对类的静态成员变量的访问。
我们再来说说同步方法和同步块。定义俺就不说了,望文生义就能知道他俩啥意思。(咱们以对象级别为例进行说明)
任何事情,认识深入了,就会一分为二的看,同步方法优点很明显,用起来简单,不用考虑那么多,将整个方法同步了。那试问,synchronized 方法,锁定的是哪个对象呢?我又开始绕弯子了,是调用这个同步方法对象。也就是说,当一个对象 P1在不同的线程中执行这个同步方法时,它们之间会形成互斥,达到同步的效果;但是这个对象所属的Class所产生的另一对象P2却可以任意调用这个被加 了synchronized关键字的方法.这么说大家是否觉得又清楚了些?!同步方法实质是将synchronized作用于object reference。――那个拿到了P1对象锁的线程,才可以调用P1的同步方法,而对P2而言,P1这个锁与它毫不相干,程序也可能在这种情形下摆脱同步机制的控制,造成数据混乱:那说说同步方法的不好的地方吧,一般在计算机里面,在研究到一定程度上的,越是大脑考虑起来简单的,那么一般就伴随着性能的差劲和灵活性的丢失,看看同步方法,将一个大的方法声明为synchronized 将会大大影响效率,咱们看一个极端的例子,若将线程类的方法 run() 声明为 synchronized ,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何 synchronized 方法的调用都永远不会成功。如果我们只是想同步某一个方法里面的一些程序片段,同步方法也是没有办法直接办到的,除非,除非你将这个程序片段单独一个方法除去,如果你真单独除去了,应该会被同行鄙视吧@__@;有需求,就会有解决方案,很多技术的进步一般都不是因为突发奇想,空穴来风,都是要做事的,都是因为有这样的需求,才会有活力。Java 为我们提供了更好的解决办法,那就是 synchronized 块。
同步块,不解释了。用法是: synchronized(this){/*区块*/},它的作用域是当前对象。这时锁就是对象,谁拿到这个锁谁就可以运行它所控制的那段代码。为啥这样,也不解释了,上面阐述的很清楚啦,如有问题,评论文章,我给回复。
当然,有的时候你需要同步操作类里面的一个成员变量,而这个类的其他字段和方法你不想锁住,因为这会大大的损耗性能,你问我可以吗?答案是肯定的,我既然卖关子了,就会有个肯定的答案,要不岂不是太贱了,哈哈~但是有个小小的提醒,只能 synchronized(instance)instance只能是对象,不能使基础数据类型。比如:
class Foo implements Runnable
{
private byte[] lock = new byte[0]; // 特殊的instance变量
Public void methodA(){
synchronized(lock) { //… }
}
//…..
}
静态方法是类级别的,原理同上,只是锁的不是对象,锁的是这个类型。
另外给大家说俩对共享资源的同步访问更加安全的技巧:这个不是我发现的,我也才开始细致看没多久,抄来的。
1) 定义private 的instance变量+它的 get方法,而不要定义public/protected的instance变量。如果将变量定义为public,对象在外界可以绕过同步方法的控制而直接取得它,并改动它。这也是JavaBean的标准实现方式之一。
2)如 果instance变量是一个对象,如数组或ArrayList什么的,那上述方法仍然不安全,因为当外界对象通过get方法拿到这个instance对 象的引用后,又将其指向另一个对象,那么这个private变量也就变了,岂不是很危险。 这个时候就需要将get方法也加上synchronized同步,并且,只返回这个private对象的clone()――这样,调用端得到的就是对象副本的引用了。
有一点是要提醒的,就是同步是否被继承?答案是否定的,
synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法。
一直都积极,坚强,乐观,谦虚的走在路上。
本文来源 我爱IT技术网 http://www.52ij.com/jishu/3859.html 转载请保留链接。
- 评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)
-
