当若干个线程共享主存区的资源时,将主存区的资源复制到自己的线程中使用,使用完毕后放回主内存,这时会产生线程安全问题。
如果是各个线程用自己的资源,则不会。
JVM内存模型:可见性,原子性。
1.线程安全额概念:当多个线程访问某一个类、对象或方法时,这个类、对象或方法都能表现出与单线程执行时一致的行为,那么就是线程安全的。
2.线程安全问题都是由全局变量及静态变量引起的。
3.每个线程中对全局变量、静态变量只有读操作,没有写操作,一般来说这个全局变量是线程安全的,若多个线程同时执行写操作,一般都要考虑线程同步。
Synchronized
Synchronized方法执行方式:
首先尝试获得锁
如果获得锁,则执行Synchronized的方法体内容
如果无法获得锁则等待,且不断尝试去获得锁,一旦锁被释放,则多个线程或同时去尝试获得锁,造成锁竞争问题。
锁竞争问题,在高并发、线程数量高时会引起CPU占用居高不下,或直接宕机
类锁和对象锁
Synchronized作用在非静态方法上代表的是对象锁,一个对象一把锁,多个对象之间不会发生锁竞争。
Synchronized作用在静态方法上则升级为类锁,所有对象共享一把锁,存在锁竞争。
同步和异步
同步:必须等待方法执行完毕后,才能向下执行,共享资源访问的时候,为了保证线程安全,必须同步。
异步:不用等待其他方法执行完毕,即可立即执行,例如Ajax异步。
对象锁的同步和异步
对象锁只针对Synchronized修饰的方法生效,对象中的所有Synchronized都会同步执行,而非Synchronized方法异步执行
脏读
多个线程访问同一个资源,在一个线程修改数据的过程中,有另外的线程来读取数据,就会引起脏读。
为了避免脏读,一定要保证数据修改的操作的原子性,也就是说,不仅要对数据的修改方法加锁,也要对读数据的方法加锁,这样才能做到对读取操作的同步控制。
Synchronized锁重入
同一个对象内得多个Synchronized方法可以锁重入
A方法时Synchronized的 B方法也是Synchronized的
在调用A的过程中调用B 并不会发生死锁,会正常执行
父子类也可以锁重入 在子类的Synchronized方法中调用父类的Synchronized方法 也不会死锁
抛出异常释放锁
一个线程在获得锁之后执行操作,发生错误抛出异常,则自动释放锁
可以利用抛出异常,主动释放锁
程序异常时防止资源被死锁、无法释放
异常释放锁可能导致数据不一致
Synchronized代码块
可以达到更精细的控制
当前对象锁
类锁
任意对象锁
总结:同类型锁之间互斥,不同类型锁直接互不干扰
锁失效
不要在线程中修改锁对象的引用,引用会引起锁失效
例如给一个字符串对象加锁,在线程中修改了字符串,则锁会失效,因为修改了字符串,获得的是一个新的地址
但是在线程中修改锁对象的属性不会引起锁失效,例如修改一个person对象的名字,只改变了其属性,但对象地址没变,不会引起线程安全问题
总结:线程A修改了对象锁的引用,则线程B实际得到了新的对象锁,而不是锁被释放了,因此引起了线程安全问题。
并发与死锁
指两个或者两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞现象,若无外力作用,他们永远不会被推进下去。
线程之间通讯
每个线程都是独立运行的个体,线程通讯能让多个线程协同工作
Object类中的wait\notify方法可以实现线程间的通讯
wait\notify必须和Synchronized一同使用
wait释放锁 notify只是发出通知 但不释放锁