java多线程开发的同步机制有哪些?
标签:分类:
一、关键词:
线程(Thread)、线程安全(thread-safe)、并发(concurrent)
同步的(synchronous),异步的(asynchronous),
Volatile(易变的)、atomic(原子的)、share(由* * *)共享
二、概述背景:
阅读和写作一次* * *和享受文件写作。哇,好家伙,我一路找出了这些零碎的知识点。由此,Google和Google阅读了《Java参考书》和《有效Java第二版》,特此总结,供以后工作和学习使用。
第三,概念:
1.我必须什么时候同步?什么是同步?怎么同步?
为了保持跨线程的正确可见性,只要非final变量在几个线程之间共享,就必须使用synchronized(或volatile)来确保一个线程可以看到另一个线程所做的更改。
同步对于线程间的可靠通信和互斥访问是必要的。这是由于java语言规范的内存模型,它规定了一个线程所做的更改何时以及如何对其他线程可见。
因为多线程将异步行为引入到程序中,所以必须有一种方法在需要时强制同步。例如,如果两个线程想要通信并想要共享一个复杂的数据结构,比如一个链表,它们需要
确保两者不冲突,也就是必须防止线程B在线程A读取数据的同时将数据写入链表(A获得锁,B必须等待A释放锁)。
为了实现这个目标,java在一个旧的进程同步模型的基础上实现了一个巧妙的方案——Monitor:Monitor是一个控制机制,可以认为是一个
只能装一根线的小盒子。一旦一个线程进入监视器,其他线程必须等待,直到该线程退出监视器。这样,一个班长就能保证* * *在中享有资源。
一次只能由一个线程使用。这种方式叫做同步。(一旦一个线程进入一个实例的任何同步方法,其他线程将无法进入同一实例的其他同步方法,但该实例
的异步方法仍然可以调用)。
错误理解:同步是指几个线程可以同时访问。
同步和多线程:没有多线程环境,就不需要同步;在多线程环境中,同步是不必要的。
锁提供了两个主要特性:互斥和可见性。
互斥意味着一次只允许一个线程持有一个特定的锁,所以这个特性可以用来实现对* * *共享数据的协同访问协议,这样一次只有一个线程可以使用* * *共享数据。
可见性更复杂,文档。它必须确保在释放锁之前对* * *享受数据所做的更改对于随后获得锁的另一个线程是可见的——如果同步机制没有提供这样的可见性保证,那么线程看到的* * *享受变量可能是在修改之前?还是不一致?这将导致许多严重的问题。
总结:为了防止多个线程并发修改同一数据,需要进行同步,否则数据会不一致(所谓:线程安全。比如哈希表和java集合框架
Vector是线程安全的。我们的大多数程序都不是线程安全的,因为没有同步,我们也不需要同步,因为大多数情况下根本没有多线程环境。
2.什么是原子(原子操作)?
Java原子操作指的是不会被中断的操作。(是互斥和可见吗?!)
那么原子操作真的能实现线程安全同步吗?事实上,有些原子操作不一定是线程安全的。
那么,什么情况下原子操作不是线程安全的呢?也许是这个原因:java线程允许线程在自己的内存区域保存变量的副本。允许线程使用本地私有副本进入
工作而不是每次都用主存?为了提高性能(愚见:虽然原子操作是线程安全的,但是每个线程在得到变量(读操作)后可以独立发挥
我自己做了副本,更新操作(写操作)不写入主存,导致其他线程不可见。
那么如何解决呢?因此,需要java同步机制。
在java中,32位或更少的赋值?它在32位硬件平台上是原子的,除了double和long之外的其他基本类型通常是
由32位表示,而double和long通常由64位表示。此外,对象引用是使用本机指针实现的,通常是32位的。对这些32位类型的操作是原创的。
孩子。
这些原语类型通常用32位或64位表示,这就引入了另一个小神话:原语类型的大小是由语言保证的。这是不对的。java语言保证了表的原始类型。
JVM中的数字范围而不是存储大小。因此,int类型总是具有相同的表号范围。32位实现可以在一个JVM上使用,而在另一个JVM上可能是64位的。在这里再次坚强起来
语气:所有平台上保证的是表号的范围,32位及更小?的操作是原子的。
3.不要混淆:同步和异步。
比如:常见的B/S模式(同步)AJAX技术(异步)
同步:提交请求-& gt;等待服务器处理-& gt;客户端浏览器在返回的过程中什么也做不了。
异步:请求由事件触发-& gt;服务器处理(这个是浏览器还能做的)->;加工完成
可见“同步”并不是这个“同步”——我们说的java中的* * *享受数据同步。
同步的对象是指行为(动作),同步的对象是指物质(* * *享数据)。
4.Java同步机制有四种实现方式:(部分参考网上资源)
①thread local②synchronized()③wait()和notify() ④ volatile。
目的:解决多线程中同一变量的访问冲突。
线程本地
ThreadLocal保证不同的线程有不同的实例,同一个线程必须有同一个实例,也就是给每个使用该变量的线程提供一个变量?每个线程可以独立地改变自己的副本,而不是与其他线程的副本冲突。
优点:它提供了一个线程安全的对象。
与其他同步机制的区别:同步机制是同步多个线程对同一资源的并发访问,并在多个线程之间进行通信;ThreadLocal是隔离多个线程的数据共享,从根本上来说是不在多个线程之间共享资源的,所以当然也没有必要让多个线程同步。
不稳定的
每次线程访问一个易失性修改成员变量时,它都被迫从* * *共享内存中重新读取该成员变量。。而且,当成员变量改变时,强制线程也会改变?写回* * *享受回忆。
优点:所以在任何时刻,两个不同的线程总是看到同一个成员变量?。
原因:Java
语言规范中指出,为了获得最好的速度,线程被允许保存成员变量的私有副本,只有当线程进入或离开同步代码块时,才能与* * * *共享成员变量的原始。
开始?对比。这样,当多个线程同时与一个对象交互时,就要注意让线程及时得到成员变量的变化。而且易变
关键字是提示VM:不能为这个成员变量保存它的私有副本,应该直接和* * *成员变量交互。
提示:对两个或多个线程访问的成员变量使用volatile。当要访问的变量已经在同步代码块中或者是常量时,就没有必要使用它。
为了提高效率,一个线程复制一个成员变量(比如A)(比如B),线程中对A的访问实际上是对B的访问..A和B的同步只在某些动作中进行,所以A和B之间存在不一致。
形势。Volatile用于避免这种情况。
Volatile告诉jvm,它修改的变量不保留副本,直接访问比较好(经常使用读操作;线程之间需要通信,这篇文章做不到)
可变变量具有同步可见性特征,但是它们没有原子特征。这意味着线程可以自动发现volatile。
最新的变量?。不稳定的
变量可以用来提供线程安全,但只能应用于非常有限的一组用例:多个变量之间还是一个变量的当前?而修改后呢?
他们之间没有约束。
只能在有限的情况下使用volatile变量代替锁。为了让volatile变量提供理想的线程安全性,必须同时满足以下两个条件:
写变量不依赖于当前?;此变量不包含在具有其他变量的不变量中。
睡眠()与等待()
Sleep是一种类似线程的方法,它使这个线程在指定的时间内暂停执行,把执行机会让给其他线程,但监控状态保持不变,之后会自动恢复。调用sleep不会释放对象锁。
Wait是Object类的一个方法。在这个对象上调用wait方法会导致这个线程放弃对象锁,进入等待锁池等待这个对象。只有在为这个对象发出notify方法(或notifyAll)后,这个线程才进入对象锁池获取对象锁,进入运行状态。
(如果变量声明为volatile,那么每次访问时都会和主存保持一致;如果在同步方法或同步块中访问某个变量,则当在方法或同步块的入口处获得锁,并在方法或同步块退出时释放锁时,该变量将被同步。)