多线程 - (二)synchronized
原创
©著作权归作者所有:来自51CTO博客作者写代码的lorre的原创作品,请联系作者获取转载授权,否则将追究法律责任
线程安全类
当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些进程将如何交替执行,并且在主调代码中不需要额外同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的
线程安全包含的特性
原子性:提供了互斥访问,同一时刻只能有一个线程对它进行操作
可见性:一个线程对主内存的修改可以及时被其他线程观察到
有序性:一个线程观察其他线程的指令执行顺序,由于指令重排序的存在,该观察结果一般杂乱无序。
原子性
保证原子性的手段通常有以下几种
当需要同步时,可能我们首先想到的就是synchronized,但是在竞争特别激烈时,synchronized可能并不是很好的选择,synchronized的使用方式大概有以下几种
修饰静态方法
此时的锁是当前类的字节码,类名.class
public static synchronized void task() {
for (int i = 0; i < 5; i++) {
System.out.println("当前线程名:" + Thread.currentThread().getName() + ",循环的次数:" + i);
try {
Thread.sleep(500);
} catch
修饰普通方法
此时的锁是当前对象,this
public synchronized void task() {
for (int i = 0; i < 5; i++) {
System.out.println("当前线程名:" + Thread.currentThread().getName() + ",循环的次数:" + i);
try {
Thread.sleep(500);
} catch
修饰代码块
此时的锁是任意对象
public void task() {
// this锁
synchronized (this) {
for (int i = 0; i < 5; i++) {
System.out.println("当前线程名:" + Thread.currentThread().getName() + ",循环的次数:" + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 任意对象
Object obj = new Object();
synchronized (obj){
}
// 字节码
synchronized (this.getClass()){
}
// 字节码
synchronized
注意
锁内部代码发生异常,锁自动释放
synchronized会保证共享变量的可见性(保证同步)
java内存模型中关于synchronized有两条规定
1、线程解锁前,必须把共享变量的最新值刷新到主内存
2、线程加锁时将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值(加锁与解锁必须是同一把锁)
不要使用String的常量加锁,会出现死循环问题
// 不要这么使用
synchronized ("字符串常量"){
}
synchronized拥有锁重入功能
也就是在使用synchronized时,当一个线程得到一个对象的锁之后,再次请求此对象时是可以再次得到该对象的锁的
public synchronized void methodA(){
methodB();
}
public synchronized void methodB(){
methodC();
}
public synchronized void methodC(){
}