打个比方:一个object就像一个大房子,大门永远打开。房子里有 很多房间(也就是方法)。这些房间有上锁的(synchronized方法), 和不上锁之分(普通方法)。房门口放着一把钥匙(key),这把钥匙可以打开所有上锁的房间。
另外我把所有想调用该对象方法的线程比喻成想进入这房子某个 房间的人。所有的东西就这么多了,下面我们看看这些东西之间如何作用的。
在此我们先来明确一下我们的前提条件。该对象至少有一个synchronized方法,否则这个key还有啥意义。当然也就不会有我们的这个主题了。
一个人想进入某间上了锁的房间,他来到房子门口,看见钥匙在那儿(说明暂时还没有其他人要使用上锁的 房间)。于是他走上去拿到了钥匙,并且按照自己 的计划使用那些房间。注意一点,他每次使用完一次上锁的房间后会马上把钥匙还回去。即使他要连续使用两间上锁的房间,中间他也要把钥匙还回去,再取回来。因此,普通情况下钥匙的使用原则是:“随用随借,用完即还。”这时其他人可以不受限制的使用那些不上锁的房间,一个人用一间可以,两个人用一间也可以,没限制。但是如果当某个人想要进入上锁的房间,他就要跑到大门口去看看了。有钥匙当然拿了就走,没有的话,就只能等了。
要是很多人在等这把钥匙,等钥匙还回来以后,谁会优先得到钥匙?Not guaranteed。象前面例子里那个想连续使用两个上锁房间的家伙,他中间还钥匙的时候如果还有其他人在等钥匙,那么没有任何保证这家伙能再次拿到。 (JAVA规范在很多地方都明确说明不保证,象
Thread.sleep()休息后多久会返回运行,相同优先权的线程那个首先被执行,当要访问对象的锁被 释放后处于等待池的多个线程哪个会优先得到,等等。我想最终的决定权是在JVM,之所以不保证,就是因为JVM在做出上述决定的时候,绝不是简简单单根据 一个条件来做出判断,而是根据很多条。而由于判断条件太多,如果说出来可能会影响JAVA的推广,也可能是因为知识产权保护的原因吧。SUN给了个不保证 就混过去了。无可厚非。但我相信这些不确定,并非完全不确定。因为计算机这东西本身就是按指令运行的。即使看起来很随机的现象,其实都是有规律可寻。学过 计算机的都知道,计算机里随机数的学名是伪随机数,是人运用一定的方法写出来的,看上去随机罢了。另外,或许是因为要想弄的确定太费事,也没多大意义,所 以不确定就不确定了吧。)
再来看看同步代码块。和同步方法有小小的不同。
1.从尺寸上讲,同步代码块比同步方法小。你可以把同步代码块看成是没上锁房间里的一块用带锁的屏风隔开的空间。
2.同步代码块还可以人为的指定获得某个其它对象的key。就像是指定用哪一把钥匙才能开这个屏风的锁,你可以用本房的钥匙;你也可以指定
用另一个房子的钥匙才能开,这样的话,你要跑到另一栋房子那儿把那个钥匙拿来,并用那个房子的钥匙来打开这个房子的带锁的屏风。
记住你获得的那另一栋房子的钥匙,并不影响其他人进入那栋房子没有锁的房间。
为什么要使用同步代码块呢?我想应该是这样的:首先对程序来讲同步的部分很影响运行效率,而一个方法通常是先创建一些局部变
量,再对这些变量做一些 操作,如运算,显示等等;而同步所覆盖的代码越多,对效率的影响就越严重。因此我们通常尽量缩小其影响范围。
如何做?同步代码块。我们只把一个方法中该同 步的地方同步,比如运算。
另外,同步代码块可以指定钥匙这一特点有个额外的好处,是可以在一定时期内霸占某个对象的key。还记得前面说过普通情况下钥
匙的使用原则吗。现在不是普通情况了。你所取得的那把钥匙不是永远不还,而是在退出同步代码块时才还。
还用前面那个想连续用两个上锁房间的家伙打比方。怎样才能在用完一间以后,继续使用另一间呢。用同步代码块吧。先创建另外
一个线程,做一个同步代码 块,把那个代码块的锁指向这个房子的钥匙。然后启动那个线程。只要你能在进入那个代码块时抓到这房子的钥匙
,你就可以一直保留到退出那个代码块。也就是说 你甚至可以对本房内所有上锁的房间遍历,甚至再sleep(10*60*1000),而房门口却还有
1000个线程在等这把钥匙呢。很过瘾吧。
在此对sleep()方法和钥匙的关联性讲一下。一个线程在拿到key后,且没有完成同步的内容时,如果被强制sleep()了,那key还一
直在 它那儿。直到它再次运行,做完所有同步内容,才会归还key。记住,那家伙只是干活干累了,去休息一下,他并没干完他要干的事。为
了避免别人进入那个房间 把里面搞的一团糟,即使在睡觉的时候他也要把那唯一的钥匙戴在身上。
最后,也许有人会问,为什么要一把钥匙通开,而不是一个钥匙一个门呢?我想这纯粹是因为复杂性问题。一个钥匙一个门当然更
安全,但是会牵扯好多问题。钥匙 的产生,保管,获得,归还等等。其复杂性有可能随同步方法的增加呈几何级数增加,严重影响效率。这也
算是一个权衡的问题吧。为了增加一点点安全性,导致效 率大大降低,是多么不可取啊。
synchronized的一个简单例子
public class TextThread {
public static void main(String[] args) {
TxtThread tt = new TxtThread();
new Thread(tt).start();
new Thread(tt).start();
new Thread(tt).start();
new Thread(tt).start();
}
}
class TxtThread implements Runnable {
int num = 100;
String str = new String();
public void run() {
synchronized (str) {
while (num > 0) {
try {
Thread.sleep(1);
} catch (Exception e) {
e.getMessage();
}
System.out.println(Thread.currentThread().getName()
+ “this is ” + num–);
}
}
}
}
上面的例子中为了制造一个时间差,也就是出错的机会,使用了Thread.sleep(10)
Java对多线程的支持与同步机制深受大家的喜爱,似乎看起来使用了synchronized关键字就可以轻松地解决多线程共享数据同步问题。到底如
何?――还得对synchronized关键字的作用进行深入了解才可定论。
总的说来,synchronized关键字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。如果再细的分类,
synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。
在进一步阐述之前,我们需要明确几点:
A.无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其
他线程的对象访问。
B.每个对象只有一个锁(lock)与之相关联。
C.实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
接着来讨论synchronized用到不同地方对代码产生的影响:
假设P1、P2是同一个类的不同对象,这个类中定义了以下几种情况的同步块或同步方法,P1、P2就都可以调用它们。
1. 把synchronized当作函数修饰符时,示例代码如下:
Public synchronized void methodAAA()
{
//….
}
这也就是同步方法,那这时synchronized锁定的是哪个对象呢?它锁定的是调用这个同步方法对象。也就是说,当一个对象P1在不同的线程中
执行这个同步方法时,它们之间会形成互斥,达到同步的效果。但是这个对象所属的Class所产生的另一对象P2却可以任意调用这个被加了
synchronized关键字的方法。
上边的示例代码等同于如下代码:
public void methodAAA()
{
synchronized (this) // (1)
{
//…..
}
}
(1)处的this指的是什么呢?它指的就是调用这个方法的对象,如P1。可见同步方法实质是将synchronized作用于object reference。――那个
拿到了P1对象锁的线程,才可以调用P1的同步方法,而对P2而言,P1这个锁与它毫不相干,程序也可能在这种情形下摆脱同步机制的控制,造
成数据混乱:(
2.同步块,示例代码如下:
public void method3(SomeObject so)
{
synchronized(so)
{
//…..
}
}
这时,锁就是so这个对象,谁拿到这个锁谁就可以运行它所控制的那段代码。当有一个明确的对象作为锁时,就可以这样写程序,但当没有明
确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的instance变量(它得是一个对象)来充当锁:
class Foo implements Runnable
{
private byte[] lock = new byte[0]; // 特殊的instance变量
Public void methodA()
{
synchronized(lock) { //… }
}
//…..
}
注:零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock
= new Object()则需要7行操作码。
3.将synchronized作用于static 函数,示例代码如下:
Class Foo
{
public synchronized static void methodAAA() // 同步的static 函数
{
//….
}
public void methodBBB()
{
synchronized(Foo.class) // class literal(类名称字面常量)
}
}
代码中的methodBBB()方法是把class literal作为锁的情况,它和同步的static函数产生的效果是一样的,取得的锁很特别,是当前调用这
个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了)。
记得在《Effective Java》一书中看到过将 Foo.class和 P1.getClass()用于作同步锁还不一样,不能用P1.getClass()来达到锁这个Class的
目的。P1指的是由Foo类产生的对象。
可以推断:如果一个类中定义了一个synchronized的static函数A,也定义了一个synchronized 的instance函数B,那么这个类的同一对象Obj
在多线程中分别访问A和B两个方法时,不会构成同步,因为它们的锁都不一样。A方法的锁是Obj这个对象,而B的锁是Obj所属的那个Class。
小结如下:
搞清楚synchronized锁定的是哪个对象,就能帮助我们设计更安全的多线程程序。
还有一些技巧可以让我们对共享资源的同步访问更加安全:
1. 定义private 的instance变量+它的 get方法,而不要定义public/protected的instance变量。如果将变量定义为public,对象在外界可以
绕过同步方法的控制而直接取得它,并改动它。这也是JavaBean的标准实现方式之一。
2. 如果instance变量是一个对象,如数组或ArrayList什么的,那上述方法仍然不安全,因为当外界对象通过get方法拿到这个instance对象
的引用后,又将其指向另一个对象,那么这个private变量也就变了,岂不是很危险。 这个时候就需要将get方法也加上synchronized同步,并
且,只返回这个private对象的clone()――这样,调用端得到的就是对象副本的引用了
分享到:
相关推荐
Java中的`synchronized`关键字是多线程编程中的一个重要概念,用于控制并发访问共享资源时的同步机制。在Java中,当多个线程试图同时访问和修改同一块代码或数据时,可能会导致数据不一致的问题。为了解决这个问题,...
总的来说,理解并合理使用`synchronized`对于编写高效、安全的多线程Java程序至关重要。在实际开发中,应根据具体需求选择合适的同步机制,避免过度使用导致性能下降。同时,配合其他并发工具类,可以实现更灵活、...
Java中的`synchronized`关键字是用于实现线程同步的关键机制,它的主要目的是确保在多线程环境中,对共享资源的访问能够保持数据的一致性和完整性。本文将深入探讨`synchronized`的两种主要用法:synchronized方法和...
Java中的`synchronized`关键字是多线程编程中用于同步控制的关键元素,它的主要目标是解决并发环境下多个线程对共享资源的访问冲突。在Java中,由于线程共享内存空间,如果没有适当的同步机制,可能会导致数据不一致...
总结,`synchronized`是Java中实现线程安全的关键工具,理解其工作原理和使用方式对于编写高效、安全的多线程程序至关重要。然而,在实际应用中,我们需要根据具体场景选择最合适的并发控制手段,平衡性能和安全性。
在Java编程语言中,`...总结,`java synchronized demo`是一个关于如何在Java中使用`synchronized`关键字进行线程同步的示例,通过理解和实践这个例子,开发者可以更好地掌握多线程环境下数据安全和同步的重要性。
在多线程编程中,确保线程安全是至关重要的。Java提供了多种机制来处理并发问题,其中synchronized...通过深入理解synchronized关键字,开发者可以更好地处理Java中的并发问题,构建出更加健壮和高效的多线程应用程序。
本文将详细介绍 Java 中 synchronized 的正确使用方法,通过示例代码和实践案例,帮助读者深入理解 synchronized 的使用方法和原理。 一、并行和锁 并行是一种提高程序效率的方式,它允许多个线程同时执行,提高...
Java中的`synchronized`关键字是多线程编程中的一个重要概念,用于控制并发访问共享资源,以保证数据的一致性和完整性。本文将深入解析`synchronized`在Java中的应用和工作原理。 一、`synchronized`的基本用法 `...
`synchronized`是Java中用于实现线程互斥的关键字,它提供了对共享资源的独占访问。当一个线程进入由`synchronized`修饰的方法或代码块时,其他试图进入同一锁的线程将被阻塞,直到该线程完成其操作并释放锁。 在这...
Java中的`synchronized`关键字是实现线程同步的关键,它的实现原理涉及到多个方面,包括Java对象头、Monitor(监视器锁)以及一系列的锁优化策略。本文将深入探讨这些概念。 首先,`synchronized`用于保证多线程...
理解 `synchronized` 的工作原理和用法对于编写线程安全的 Java 代码至关重要。然而,过度使用 `synchronized` 可能会导致性能下降,因为它会阻塞线程。因此,开发者需要谨慎地权衡同步的粒度,尽可能减少锁的范围,...
Java中的`synchronized`关键字是实现线程同步的关键机制,它保证了在多线程环境下,对共享资源的访问是互斥的,防止了数据竞争和不一致的问题。synchronized通过锁的概念来控制对代码块或方法的访问,分为两种形式:...
在Java编程语言中,多线程是并发执行多个任务或操作的一种方式,这对于优化系统性能、提高资源利用率具有重要意义。...对于开发者来说,理解和熟练掌握`synchronized`是编写高效、安全的多线程Java程序的基础。
Java 中的 Lock 和 Synchronized 的区别 Java 语言中有很多相似关键字或相似意义的字,但 lock 和 synchronized 是两个最容易混淆的关键字。它们都是锁的意思,都是为了线程安全性、应用合理性和运行效率的。下面...
Java中的`synchronized`...理解`synchronized`的工作原理和基本规则是编写安全的多线程Java代码的关键。在实际编程中,应谨慎使用`synchronized`,因为它会降低程序的并发性能,但却是保证线程安全不可或缺的工具。
【Java同步之synchronized解析】 Java中的`synchronized`关键字是实现多线程同步的重要机制,它确保了在并发环境中对共享资源的...理解并熟练掌握`synchronized`的使用对于编写高性能、线程安全的Java程序至关重要。
Java 线程同步机制中 synchronized 关键字的理解 Java 的线程同步机制是为了解决多个线程共享同一片存储空间所带来的访问冲突问题。其中,synchronized 关键字是 Java 语言中解决这种冲突的重要机制。 ...
在Java编程语言中,`synchronized`是一个非常重要的关键字,它主要用于实现同步控制机制。通过使用`synchronized`,可以确保同一时刻只有一个线程能够访问被标记为同步的方法或代码块,从而避免多线程环境下的数据不...