以前记录摘要一直是印象笔记来记录,但也经常辗转于CSDN,把这些年的经验分享给大家
- 线程同步机制
要跨线程维护正确的可见性,只要在几个线程之间共享非 final 变量,就必须使用 synchronized(或 volatile)以确保一个线程可以看见另一个线程做的更改。
为了在线程之间进行可靠的通信,也为了互斥访问,同步是必须的。这归因于java语言规范的内存模型,它规定了:一个线程所做的变化何时以及如何变成对其它线程可见。
因为多线程将异步行为引进程序,所以在需要同步时,必须有一种方法强制进行。例如:如果2个线程想要通信并且要共享一个复杂的数据结构,如链表,此时需要确保它们互不冲突,也就是必须阻止B线程在A线程读数据的过程中向链表里面写数据(A获得了锁,B必须等A释放了该锁)。
为了达到这个目的,java在一个旧的的进程同步模型——监控器(Monitor)的基础上实现了一个巧妙的方案:监控器是一个控制机制,可以认为是一个很小的、只能容纳一个线程的盒子,一旦一个线程进入监控器,其它的线程必须等待,直到那个线程退出监控为止。通过这种方式,一个监控器可以保证共享资源在同一时刻只可被一个线程使用。这种方式称之为同步。(一旦一个线程进入一个实例的任何同步方法,别的线程将不能进入该同一实例的其它同步方法,但是该实例的非同步方法仍然能够被调用)
-
同步的关键字
thread(线程)、thread-safe(线程安全)、intercurrent(并发的)
synchronized(同步的)、asynchronized(异步的)
volatile(易变的)、atomic(原子的)、share(共享) -
锁提供了两种主要特性:
互斥(mutual exclusion) 和可见性(visibility)。
互斥即一次只允许一个线程持有某个特定的锁,因此可使用该特性实现对共享数据的协调访问协议,这样,一次就只有一个线程能够使用该共享数据。
可见性要更加复杂一些,documents它必须确保释放锁之前对共享数据做出的更改对于随后获得该锁的另一个线程是可见的 —— 如果没有同步机制提供的这种可见性保证,线程看到的共享变量可能是修改前的值或不一致的值,这将引发许多严重问题。 -
结论
为防止多个线程并发对同一数据的修改,因此需要同步,否则会造成数据不一致(就是所谓的:线程安全。如java集合框架中Hashtable和Vector是线程安全的。我们的大部分程序都不是线程安全的,因为没有进行同步,而且我们没有必要,因为大部分情况根本没有多线程环境)。
- 原子性
Java原子操作是指:不会被打断地的操作。
难道原子操作就可以真的达到线程安全同步效果了吗?实际上有一些原子操作不一定是线程安全的。
那么,原子操作在什么情况下不是线程安全的呢?也许是这个原因导致的:java线程允许线程在自己的内存区保存变量的副本。允许线程使用本地的私有拷贝进行工作而非每次都使用主存的值是为了提高性能(本人愚见:虽然原子操作是线程安全的,可各线程在得到变量(读操作)后,就是各自玩弄自己的副本了,更新操作(写操作)因未写入主存中,导致其它线程不可见)。
因此需要通过java同步机制 - 同步/异步
例:普通B/S模式(同步)AJAX技术(异步)
同步:提交请求->等待服务器处理->处理完返回 这个期间客户端浏览器不能干任何事
异步:请求通过事件触发->服务器处理(这是浏览器仍然可以作其他事情)->处理完毕 - Java同步机制有4种实现方式
① ThreadLocal ② synchronized( ) ③ wait() 与 notify() ④ volatile
目的:都是为了解决多线程中的对同一变量的访问冲突
demo
public class SynChronizedTest {
static void synchronizedTest(Thread thread) {
System.out.println("start" + thread.getName());
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("end" + thread.getName());
}
/**
* 同步方法
*
* @param thread
*/
synchronized static void synchronizedtest1(Thread thread) {
System.out.println("start" + thread.getName());
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("end" + thread.getName());
}
/**
* 同步代码块
*
* @param thread
*/
static void synchronizedTest2(Thread thread) {
synchronized (SynChronizedTest.class) {
System.out.println("start" + thread.getName());
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("end" + thread.getName());
}
}
/**
* 同步方式三:使用同步对象锁
*
* @param thread
*/
private static Object lock = new Object();
static void synchronizedTest3(Thread thread) {
synchronized (lock) {
System.out.println("start" + thread.getName());
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("end" + thread.getName());
}
}
public static void main(String[] args) {
// 启动3个线程
for (int i = 0; i <= 3; i++) {
new Thread() {
@Override
public void run() {
synchronizedTest(this);
// synchronizedtest1(this);
// synchronizedTest2(this);
// synchronizedTest3(this);
}
}.start();
}
}
}
运行结果