并发问题的出现
public class SynchronizedTest extends Thread{
private static int index = 1;
private static final int MAX=50;
@Override
public void run() {
while (index <= MAX) {
System.out.println(Thread.currentThread().getName()+"得到数字:"+(index++));
}
}
public static void main(String[] args) {
SynchronizedTest test1 = new SynchronizedTest();
SynchronizedTest test2 = new SynchronizedTest();
SynchronizedTest test3 = new SynchronizedTest();
SynchronizedTest test4 = new SynchronizedTest();
SynchronizedTest test5 = new SynchronizedTest();
test1.start();
test2.start();
test3.start();
test4.start();
test5.start();
}
}
这是一个典型的案列,上面可能会出现多种并发的错误,例如 跳号 重号或者超过最大值
可以使用synchronized来解决并发的问题
synchronized概念
- 原理:使用锁的机制来实现同步
锁机制的特性:
1.互斥性:即在同一时间只允许一个线程持有某个对象锁
2.可见性:必须确保在锁被释放之前,对共享变量所做的修改,对于随后获得该锁的另一个线程是可见的
synchronized用法
-
同步方法
(1)同步非静态方法 Public synchronized void methodName(){ …… } (2)同步静态方法 Public synchronized static void methodName(){ …… }
-
同步代码块
synchronized(this|object) {} synchronized(类.class) {}
synchronized原理分析
- 线程堆栈分析
jconsole
jstack pid
-
JVM指令分析(javap -V 反编译)
public void run(); descriptor: ()V flags: ACC_PUBLIC Code: stack=3, locals=4, args_size=1 0: aload_0 1: dup 2: astore_1 3: monitorenter 锁的入口 4: getstatic #2 // Field index:I 7: sipush 500 10: if_icmpgt 77 13: getstatic #4 // Field java/util/concurrent/TimeUnit.SECONDS:Ljava/util/concurrent/TimeUnit; 16: ldc2_w #5 // long 3l 19: invokevirtual #7 // Method java/util/concurrent/TimeUnit.sleep:(J)V 22: goto 30 25: astore_2 26: aload_2 27: invokevirtual #9 // Method java/lang/InterruptedException.printStackTrace:()V 30: getstatic #2 // Field index:I 33: iconst_1 34: iadd 35: putstatic #2 // Field index:I 38: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream; 41: new #11 // class java/lang/StringBuilder 44: dup 45: invokespecial #12 // Method java/lang/StringBuilder."<init>":()V 48: invokestatic #13 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread; 51: invokevirtual #14 // Method java/lang/Thread.getName:()Ljava/lang/String; 54: invokevirtual #15 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 57: ldc #16 // String 得到数字: 59: invokevirtual #15 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 62: getstatic #2 // Field index:I 65: invokevirtual #17 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 68: invokevirtual #18 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 71: invokevirtual #19 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 74: goto 4 77: aload_1 78: monitorexit 锁的出口 79: goto 87 82: astore_3 83: aload_1 84: monitorexit 85: aload_3 86: athrow 87: return
原理:monitor一个线程占有,其他线程请求时会进入BOLCK,直到monitor为0,触发Monitorexit
3.使用synchronized注意的问题
与moniter关联的对象不能为空
synchronized的作用域不能太大
不同的monitor企图锁相同的方法
注意多个锁的交叉,异能会导致死锁
JVM对synchronized的优化
1.锁的进化
偏向锁:在对象第一次被某一线程占有的时候,锁标记01,写入线程号。
轻量级锁:线程有交替使用的时候,互斥性一般 当偏向锁CAS失败 锁标记为00,升级为轻量级锁
重量级锁: 当等待时间长,强互斥,锁标记为10
自旋锁:竞争失败的时候,不是马上转化级别,而是执行几次空循环5 10
锁消除:JIT(即时编译器)在编译的时候吧不必要的锁去掉
2.一个对象包含的信息
对象头和monitor(加锁的基础)
1.32/64bit的hashcode
实例变量
1.私有的变量
填充数据
1.对象的起始地址(8字节)