1.概念当多个线程访问同一个资源时,需要对资源进行加锁。即同一时间只允许一个线程访问该资源。任何线程想要执行synchronized中的代码必须先获取锁。synchronized的底层实现,JVM并没有规定必须如何实现。Hotspot在objectheader(64bits)上取出2bits记录对象是否被锁定,markword,即某个对象被锁定。每个class文件加载到内存后,会对应加载到内存中的代码生成一个Class类的对象,所以当一个静态方法被加锁时,Class类的一个对象(比如T.class)锁定。同步锁都是对象。1.同步方法和非同步方法可以同时执行().getName()+"m1开始...");尝试{Thread.sleep(5000);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"m1end...");}publicvoidm2(){尝试{线程。睡眠(2000);}catch(InterruptedExceptione){e.打印堆栈跟踪();}系统。出去。println(Thread.currentThread().getName()+"m2end...");}publicstaticvoidmain(String[]args){Tt=newT();newThread(()->t.m1(),"t1").start();newThread(()->t.m2(),"t2").start();//newThread(t::m1,"t1").start();//newThread(t::m2,"t2").start();}}/***@authorJava与算法学习:星期一*/publicclassAccount{Stringname;双倍的钱;民众synchronizedvoidset(Stringname,doublemoney){this.name=name;尝试{Thread.sleep(3000);}catch(InterruptedExceptione){e.printStackTrace();}this.money=money;}public/*synchronized*/doublegetMoney(Stringname){returnmoney;}publicstaticvoidmain(String[]args){Accountaccount=newAccount();newThread(()->account.set("zhang",100.0)).start();尝试{Thread.sleep(1000);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println(account.getMoney("张"));尝试{Thread.sleep(3000);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println(account.getMoney("张"));}}2.synchronized是一个可重入锁。一个同步方法可以调用另一个同步方法,一个线程拥有一个对象的锁,再次申请时仍然会得到该对象的锁比如父类的一个方法是synchronized的,子类重写了这个方法,在方法中调用了父类的方法,这个时候就可以调用了。如果不是可重入锁,显然有问题。/***@authorJava和算法学习:星期一*/publicclassT{publicsynchronizedvoidm(){System.out.println("mstart...");尝试{Thread.sleep(1000);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("m结束...");}}classChildextendsT{@Overridepublicsynchronizedvoidm(){System.out.println("childmstart...");超级米();System.out.println("子m结束...");}publicstaticvoidmain(String[]args){newChild().m();}}3.locked方法出现异常会释放锁/***@authorJava与算法学习:Monday*/publicclassT{intcount;publicsynchronizedvoidm(){while(true){count++;System.out.println(Thread.currentThread().getName()+"count="+count);尝试{Thread.sleep(1000);}catch(InterruptedExceptione){e.printStackTrace();}如果(计数==3){//如果这里抛出异常,就会释放锁。如果希望不被释放,可以在这里进行catch处理,然后让循环继续下去inti=1/0;}}}publicstaticvoidmain(String[]args){Tt=newT();newThread(()->t.m(),"t1").start();newThread(()->t.m(),"t2").start();}}4.synchronized(Object)不能锁String常量,Integer,Long等2.JDK(1.5之前)底层实现早期是重量级锁,都是找OS申请锁改进:锁升级sync(this)Hotspot实现1、当第一个线程访问时,markword先在对象头记录本线程的线程号,此时没有锁。这称为偏向锁(即偏向于你)。当同一个线程再次访问时,可以直接使用,效率很高。2.当第二个线程访问时(即有线程争用时),升级为自旋锁,占用CPU等待(不会进入CPU的等待队列),但不访问OS,所以在用户态解决了加锁的问题,不经过内核态,加锁和解锁的效率比通过内核态要高。3.默认情况下,锁轮换10次后会再次升级,升级为重量级锁,即去OS申请资源加锁,这个线程会变成waiting状态,进入CPU的等待队列(不再占用CPU资源)。4.锁只能升级,不能降级。由此可见,执行时间长的时候使用重量级锁,执行时间短的时候使用自旋锁;重量级锁用于多线程,自旋锁用于少数线程。加锁代码执行时间长,线程数多,加锁代码执行时间短,线程数少,使用重量级锁,使用自旋锁。三、锁的其他相关知识1、锁的细化count++前后还有一些业务逻辑。这时候synchronized不需要加在method中,直接加在count++中即可。(2)锁的粗化比如一个方法中有很多细锁,可以对锁进行粗化,即在方法中加锁。2.锁定对象的改变锁定了一个对象o。如果o的属性发生变化,不会影响锁的使用;但如果o成为另一个对象,则锁定的对象将发生变化。如果你不想o改变,你可以将它定义为最终类型。
