当前位置: 首页 > news >正文

土建设计网站网线制作实训报告心得体会

土建设计网站,网线制作实训报告心得体会,讯美智能网站建设,代理网课synchronized的三种使用方式 **1.修饰实例方法,**作用于当前实例加锁#xff0c;进入同步代码前要获得当前实例的锁。 没有问题的写法#xff1a; public class AccountingSync implements Runnable{//共享资源(临界资源)static int i0;/*** synchronized 修饰实例方法*/p…synchronized的三种使用方式 **1.修饰实例方法,**作用于当前实例加锁进入同步代码前要获得当前实例的锁。 没有问题的写法 public class AccountingSync implements Runnable{//共享资源(临界资源)static int i0;/*** synchronized 修饰实例方法*/public synchronized void increase(){i;}Overridepublic void run() {for(int j0;j1000000;j){increase();}}public static void main(String[] args) throws InterruptedException {AccountingSync instancenew AccountingSync();Thread t1new Thread(instance);Thread t2new Thread(instance);t1.start();t2.start();t1.join();t2.join();System.out.println(i);}/*** 输出结果:* 2000000*/ }因为这段代码中只输入了一个AccountingSync实例。 下面是有问题的写法 public class AccountingSyncBad implements Runnable{static int i0;public synchronized void increase(){i;}Overridepublic void run() {for(int j0;j1000000;j){increase();}}public static void main(String[] args) throws InterruptedException {//new新实例Thread t1new Thread(new AccountingSyncBad());//new新实例Thread t2new Thread(new AccountingSyncBad());t1.start();t2.start();//join含义:当前线程A等待thread线程终止之后才能从thread.join()返回t1.join();t2.join();System.out.println(i);} }在上述代码中出现了一个严重的错误虽然我们使用了sychronized修饰了increase方法但new了两个不同的实例对象这就意味着会出现两个不同的实例对象锁这样的话ti和t2都会进入各自的对象锁因此线程安全就得不到保证。想要解决这个问题就需要将sychronized作用于静态的increase方法。 2.修饰静态方法,作用于当前类对象加锁进入同步代码前要获得当前类对象的锁。 当sychronized使用静态方法时对象锁就会是当前类的class对象锁。因为静态成员不属于任何一个实例对象所以通过class对象锁可以控制静态 成员的并发操作。 如果一个线程A调用一个实例对象的非static synchronized方法而线程B需要调用这个实例对象所属类的静态 synchronized方法是允许的不会发生互斥现象因为访问静态 synchronized 方法占用的锁是当前类的class对象而访问非静态 synchronized 方法占用的锁是当前实例对象锁看如下代码: public class AccountingSyncClass implements Runnable{static int i0;/*** 作用于静态方法,锁是当前class对象,也就是* AccountingSyncClass类对应的class对象*/public static synchronized void increase(){i;}/*** 非静态,访问时锁不一样不会发生互斥*/public synchronized void increase4Obj(){i;}Overridepublic void run() {for(int j0;j1000000;j){increase();}}public static void main(String[] args) throws InterruptedException {//new新实例Thread t1new Thread(new AccountingSyncClass());//new心事了Thread t2new Thread(new AccountingSyncClass());//启动线程t1.start();t2.start();t1.join();t2.join();System.out.println(i);} }这种情况下可能会发现线程安全问题(操作了共享静态变量i)。 3.修饰代码块指定加锁对象对给定对象加锁进入同步代码库前要获得给定对象的锁。 synchronized同步代码块 public class AccountingSync implements Runnable{static AccountingSync instancenew AccountingSync();static int i0;Overridepublic void run() {//省略其他耗时操作....//使用同步代码块对变量i进行同步操作,锁对象为instancesynchronized(instance){for(int j0;j1000000;j){i;}}}public static void main(String[] args) throws InterruptedException {Thread t1new Thread(instance);Thread t2new Thread(instance);t1.start();t2.start();t1.join();t2.join();System.out.println(i);} }当然除了instance作为对象外我们还可以使用this对象(代表当前实例)或者当前类的class对象作为锁如下代码 //this,当前实例对象锁 synchronized(this){for(int j0;j1000000;j){i;} }//class对象锁 synchronized(AccountingSync.class){for(int j0;j1000000;j){i;} }synchronized底层语义原理 Java 虚拟机中的同步(Synchronization)基于进入和退出管程(Monitor)对象实现 无论是显式同步(有明确的 monitorenter 和 monitorexit 指令,即同步代码块)还是隐式同步都是如此。 同步方法 并不是由 monitorenter 和 monitorexit 指令来实现同步的而是由方法调用指令读取运行时常量池中方法的 ACC_SYNCHRONIZED 标志来隐式实现的 理解Java对象头与Monitor 在JVM中对象在内存中的布局分为三块区域对象头、实例数据和对齐填充。 ​ 实例变量存放类的属性数据信息包括父类的属性信息如果是数组的实例部分还包括数组的长度这部分内存按4字节对齐。 填充数据由于虚拟机要求对象起始地址必须是8字节的整数倍。填充数据不是必须存在的仅仅是为了字节对齐 顶部则是Java头对象它实现synchronized的锁对象的基础。 synchronized使用的锁对象是存储在Java对象头里的jvm中采用2个字来存储对象头(如果对象是数组则会分配3个字多出来的1个字记录的是数组长度)其主要结构是由Mark Word 和 Class Metadata Address 组成其结构说明如下表 ​ 其中Mark Word在默认情况下存储着对象的HashCode、分代年龄、锁标记位等以下是32位JVM的Mark Word默认存储结构 由于对象头的信息是与对象自身定义的数据没有关系的额外存储成本因此考虑到JVM的空间效率Mark Word 被设计成为一个非固定的数据结构以便存储更多有效的数据它会根据对象本身的状态复用自己的存储空间如32位JVM下除了上述列出的Mark Word默认存储结构外还有如下可能变化的结构 主要分析一下重量级锁也就是通常说synchronized的对象锁锁标识位为10其中指针指向的是monitor对象也称为管程或监视器锁的起始地址。每个对象都存在着一个 monitor 与之关联对象与其 monitor 之间的关系有存在多种实现方式如monitor可以与对象一起创建销毁或当线程试图获取对象锁时自动生成但当一个 monitor 被某个线程持有后它便处于锁定状态。 在Java虚拟机(HotSpot)中monitor是由ObjectMonitor实现的其主要数据结构如下位于HotSpot虚拟机源码ObjectMonitor.hpp文件C实现的。 ObjectMonitor() {_header NULL;_count 0; //记录个数_waiters 0,_recursions 0;_object NULL;_owner NULL;_WaitSet NULL; //处于wait状态的线程会被加入到_WaitSet_WaitSetLock 0 ;_Responsible NULL ;_succ NULL ;_cxq NULL ;FreeNext NULL ;_EntryList NULL ; //处于等待锁block状态的线程会被加入到该列表_SpinFreq 0 ;_SpinClock 0 ;OwnerIsThread 0 ;}ObjectMonitor中有两个队列_WaitSet 和 _EntryList用来保存ObjectWaiter对象列表( 每个等待锁的线程都会被封装成ObjectWaiter对象)_owner指向持有ObjectMonitor对象的线程当多个线程同时访问一段同步代码时首先会进入 _EntryList 集合当线程获取到对象的monitor 后进入 _Owner 区域并把monitor中的owner变量设置为当前线程同时monitor中的计数器count加1若线程调用 wait() 方法将释放当前持有的monitorowner变量恢复为nullcount自减1同时该线程进入 WaitSet集合中等待被唤醒。若当前线程执行完毕也将释放monitor(锁)并复位变量的值以便其他线程进入获取monitor(锁)。 ​ monitor对象存在于每个Java对象的对象头中(存储的指针的指向)synchronized锁便是通过这种方式获取锁的也是为什么Java中任意对象可以作为锁的原因同时也是notify/notifyAll/wait等方法存在于顶级对象Object中的原因。 synchronized代码块底层原理 现在我们重新定义一个synchronized修饰的同步代码块在代码块中操作共享变量i如下 public class SyncCodeBlock {public int i;public void syncTask(){//同步代码库synchronized (this){i;}} }编译上述代码并使用javap反编译后得到字节码如下(这里我们省略一部分没有必要的信息) Classfile /Users/zejian/Downloads/Java8_Action/src/main/java/com/zejian/concurrencys/SyncCodeBlock.classLast modified 2017-6-2; size 426 bytesMD5 checksum c80bc322c87b312de760942820b4fed5Compiled from SyncCodeBlock.java public class com.zejian.concurrencys.SyncCodeBlockminor version: 0major version: 52flags: ACC_PUBLIC, ACC_SUPER Constant pool://........省略常量池中数据//构造函数public com.zejian.concurrencys.SyncCodeBlock();descriptor: ()Vflags: ACC_PUBLICCode:stack1, locals1, args_size10: aload_01: invokespecial #1 // Method java/lang/Object.init:()V4: returnLineNumberTable:line 7: 0//主要看看syncTask方法实现public void syncTask();descriptor: ()Vflags: ACC_PUBLICCode:stack3, locals3, args_size10: aload_01: dup2: astore_13: monitorenter //注意此处进入同步方法4: aload_05: dup6: getfield #2 // Field i:I9: iconst_110: iadd11: putfield #2 // Field i:I14: aload_115: monitorexit //注意此处退出同步方法16: goto 2419: astore_220: aload_121: monitorexit //注意此处退出同步方法22: aload_223: athrow24: returnException table://省略其他字节码....... } SourceFile: SyncCodeBlock.java我们主要关注字节码中的如下代码 3: monitorenter //进入同步方法 //..........省略其他 15: monitorexit //退出同步方法 16: goto 24 //省略其他....... 21: monitorexit //退出同步方法从字节码中可知同步语句块的实现使用的是monitorenter 和 monitorexit 指令其中monitorenter指令指向同步代码块的开始位置monitorexit指令则指明同步代码块的结束位置当执行monitorenter指令时当前线程将试图获取 objectref(即对象锁) 所对应的 monitor 的持有权当 objectref 的 monitor 的进入计数器为 0那线程可以成功取得 monitor并将计数器值设置为 1取锁成功。**如果当前线程已经拥有 objectref 的 monitor 的持有权那它可以重入这个 monitor (关于重入性稍后会分析)重入时计数器的值也会加 1。**倘若其他线程已经拥有 objectref 的 monitor 的所有权那当前线程将被阻塞直到正在执行线程执行完毕即monitorexit指令被执行执行线程将释放 monitor(锁)并设置计数器值为0 其他线程将有机会持有 monitor 。值得注意的是编译器将会确保无论方法通过何种方式完成方法中调用过的每条 monitorenter 指令都有执行其对应 monitorexit 指令而无论这个方法是正常结束还是异常结束。为了保证在方法异常完成时 monitorenter 和 monitorexit 指令依然可以正确配对执行编译器会自动产生一个异常处理器这个异常处理器声明可处理所有的异常它的目的就是用来执行 monitorexit 指令。从字节码中也可以看出多了一个monitorexit指令它就是异常结束时被执行的释放monitor 的指令。 synchronized方法底层原理 方法级的同步是隐式即无需通过字节码指令来控制的它实现在方法调用和返回操作之中。JVM可以从方法常量池中的方法表结构(method_info Structure) 中的 ACC_SYNCHRONIZED 访问标志区分一个方法是否同步方法。当方法调用时调用指令将会 检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置如果设置了执行线程将先持有monitor虚拟机规范中用的是管程一词 然后再执行方法最后再方法完成(无论是正常完成还是非正常完成)时释放monitor。在方法执行期间执行线程持有了monitor其他任何线程都无法再获得同一个monitor。如果一个同步方法执行期间抛 出了异常并且在方法内部无法处理此异常那这个同步方法所持有的monitor将在异常抛到同步方法之外时自动释放。 我们看看字节码层面如何实现 public class SyncMethod {public int i;public synchronized void syncTask(){i;} }使用javap反编译后的字节码如下 Classfile /Users/zejian/Downloads/Java8_Action/src/main/java/com/zejian/concurrencys/SyncMethod.classLast modified 2017-6-2; size 308 bytesMD5 checksum f34075a8c059ea65e4cc2fa610e0cd94Compiled from SyncMethod.java public class com.zejian.concurrencys.SyncMethodminor version: 0major version: 52flags: ACC_PUBLIC, ACC_SUPER Constant pool;//省略没必要的字节码//syncTask方法public synchronized void syncTask();descriptor: ()V//方法标识ACC_PUBLIC代表public修饰ACC_SYNCHRONIZED指明该方法为同步方法flags: ACC_PUBLIC, ACC_SYNCHRONIZEDCode:stack3, locals1, args_size10: aload_01: dup2: getfield #2 // Field i:I5: iconst_16: iadd7: putfield #2 // Field i:I10: returnLineNumberTable:line 12: 0line 13: 10 } SourceFile: SyncMethod.java从字节码中可以看出synchronized修饰的方法并没有monitorenter指令和monitorexit指令取得代之的确实是ACC_SYNCHRONIZED标识该标识指明了该方法是一个同步方法JVM通过该ACC_SYNCHRONIZED访问标志来辨别一个方法是否声明为同步方法从而执行相应的同步调用。操作系统实现线程之间的切换时需要从用户态转换到内核态 Java虚拟机对synchronized的优化 锁的状态总共有四种无锁状态、偏向锁、轻量级锁和重量级锁。随着锁的竞争锁可以从偏向锁升级到轻量级锁再升级的重量级锁但是锁的升级是单向的也就是说只能从低到高升级不会出现锁的降级 偏向锁 在大多数情况下锁不仅不存在多线程竞争而且总是由同一线程多次获得因此为了减少同一线程获取锁(会涉及到一些CAS操作,耗时)的代价而引入偏向锁。偏向锁的核心思想是如果一个线程获得了锁那么锁就进入偏向模式此时Mark Word 的结构也变为偏向锁结构当这个线程再次请求锁时无需再做任何同步操作即获取锁的过程这样就省去了大量有关锁申请的操作从而也就提供程序的性能。 但是对于锁竞争比较激烈的场合偏向锁就失效了偏向锁失败后并不会立即膨胀为重量级锁而是先升级为轻量级锁。 轻量级锁 量级锁能够提升程序性能的依据是“对绝大部分的锁在整个同步周期内都不存在竞争”注意这是经验数据。需要了解的是轻量级锁所适应的场景是线程交替执行同步块的场合如果存在同一时间访问同一锁的场合就会导致轻量级锁膨胀为重量级锁。 自旋锁 虚拟机为了避免线程真实地在操作系统层面挂起还会进行一项称为自旋锁的优化手段。这是基于在大多数情况下线程持有锁的时间都不会太长如果直接挂起操作系统层面的线程可能会得不偿失毕竟操作系统实现线程之间的切换时需要从用户态转换到核心态这个状态之间的转换需要相对比较长的时间时间成本相对较高因此自旋锁会假设在不久将来当前的线程可以获得锁因此虚拟机会让当前想要获取锁的线程做几个空循环(这也是称为自旋的原因)一般不会太久可能是50个循环或100循环在经过若干次循环后如果得到锁就顺利进入临界区。如果还不能获得锁那就会将线程在操作系统层面挂起这就是自旋锁的优化方式这种方式确实也是可以提升效率的。最后没办法也就只能升级为重量级锁了。 锁消除 Java虚拟机在JIT编译时(可以简单理解为当某段代码即将第一次被执行时进行编译又称即时编译)通过对运行上下文的扫描去除不可能存在共享资源竞争的锁通过这种方式消除没有必要的锁可以节省毫无意义的请求锁时间如下StringBuffer的append是一个同步方法但是在add方法中的StringBuffer属于一个局部变量并且不会被其他线程所使用因此StringBuffer不可能存在共享资源竞争的情景JVM会自动将其锁消除。 关于synchronized 可能需要了解的关键点 一个线程调用synchronized方法的同时在其方法体内部调用该对象另一个synchronized方法也就是说一个线程得到一个对象锁后再次请求该对象锁是允许的这就是synchronized的可重入性。如下 public class AccountingSync implements Runnable{static AccountingSync instancenew AccountingSync();static int i0;static int j0;Overridepublic void run() {for(int j0;j1000000;j){//this,当前实例对象锁synchronized(this){i;increase();//synchronized的可重入性}}}public synchronized void increase(){j;}public static void main(String[] args) throws InterruptedException {Thread t1new Thread(instance);Thread t2new Thread(instance);t1.start();t2.start();t1.join();t2.join();System.out.println(i);} }在获取当前实例对象锁后进入synchronized代码块执行同步代码并在代码块中调用了当前实例对象的另外一个synchronized方法再次请求当前实例锁时将被允许进而执行方法体代码这就是重入锁最直接的体现需要特别注意另外一种情况当子类继承父类时子类也是可以通过可重入锁调用父类的同步方法。注意由于synchronized是基于monitor实现的因此每次重入monitor中的计数器仍会加1。 线程中断与synchronized 正如中断二字所表达的意义在线程运行(run方法)中间打断它在Java中提供了以下3个有关线程中断的方法 //中断线程实例方法 public void Thread.interrupt();//判断线程是否被中断实例方法 public boolean Thread.isInterrupted();//判断是否被中断并清除当前中断状态静态方法 public static boolean Thread.interrupted();当一个线程处于被阻塞状态或者试图执行一个阻塞操作时使用Thread.interrupt()方式中断该线程注意此时将会抛出一个InterruptedException的异常同时中断状态将会被复位(由中断状态改为非中断状态)如下代码将演示该过程 public class InterruputSleepThread3 {public static void main(String[] args) throws InterruptedException {Thread t1 new Thread() {Overridepublic void run() {//while在try中通过异常中断就可以退出run循环try {while (true) {//当前线程处于阻塞状态异常必须捕捉处理无法往外抛出TimeUnit.SECONDS.sleep(2);}} catch (InterruptedException e) {System.out.println(Interruted When Sleep);boolean interrupt this.isInterrupted();//中断状态被复位System.out.println(interrupt:interrupt);}}};t1.start();TimeUnit.SECONDS.sleep(2);//中断处于阻塞状态的线程t1.interrupt();/*** 输出结果:Interruted When Sleepinterrupt:false*/} }除了阻塞中断的情景我们还可能会遇到处于运行期且非阻塞的状态的线程这种情况下直接调用Thread.interrupt()中断线程是不会得到任响应的如下代码将无法中断非阻塞状态下的线程 public class InterruputThread {public static void main(String[] args) throws InterruptedException {Thread t1new Thread(){Overridepublic void run(){while(true){System.out.println(未被中断);}}};t1.start();TimeUnit.SECONDS.sleep(2);t1.interrupt();/*** 输出结果(无限执行):未被中断未被中断未被中断......*/} }虽然我们调用了interrupt方法但线程t1并未被中断因为处于非阻塞状态的线程需要我们手动进行中断检测并结束程序改进后代码如下 public class InterruputThread {public static void main(String[] args) throws InterruptedException {Thread t1new Thread(){Overridepublic void run(){while(true){//判断当前线程是否被中断if (this.isInterrupted()){System.out.println(线程中断);break;}}System.out.println(已跳出循环,线程中断!);}};t1.start();TimeUnit.SECONDS.sleep(2);t1.interrupt();/*** 输出结果:线程中断已跳出循环,线程中断!*/} }简单总结一下中断两种情况一种是当线程处于阻塞状态或者试图执行一个阻塞操作时我们可以使用实例方法interrupt()进行线程中断执行中断操作后将会抛出interruptException异常(该异常必须捕捉无法向外抛出)并将中断状态复位另外一种是当线程处于运行状态时我们也可调用实例方法interrupt()进行线程中断但同时必须手动判断中断状态并编写中断线程的代码(其实就是结束run方法体的代码)。有时我们在编码时可能需要兼顾以上两种情况那么就可以如下编写 public void run(){try {//判断当前线程是否已中断,注意interrupted方法是静态的,执行后会对中断状态进行复位while (!Thread.interrupted()) {TimeUnit.SECONDS.sleep(2);}} catch (InterruptedException e) {} }中断与synchronized 线程的中断操作对于正在等待获取的锁对象的synchronized方法或者代码块并不起作用也就是对于synchronized来说如果一个线程在等待锁那么结果只有两种要么它获得这把锁继续执行要么它就保存等待即使调用中断线程的方法也不会生效。 /*** Created by zejian on 2017/6/2.* Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创]*/ public class SynchronizedBlocked implements Runnable{public synchronized void f() {System.out.println(Trying to call f());while(true) // Never releases lockThread.yield();}/*** 在构造器中创建新线程并启动获取对象锁*/public SynchronizedBlocked() {//该线程已持有当前实例锁new Thread() {public void run() {f(); // Lock acquired by this thread}}.start();}public void run() {//中断判断while (true) {if (Thread.interrupted()) {System.out.println(中断线程!!);break;} else {f();}}}public static void main(String[] args) throws InterruptedException {SynchronizedBlocked sync new SynchronizedBlocked();Thread t new Thread(sync);//启动后调用f()方法,无法获取当前实例锁处于等待状态t.start();TimeUnit.SECONDS.sleep(1);//中断线程,无法生效t.interrupt();} }我们在SynchronizedBlocked构造函数中创建一个新线程并启动获取调用f()获取到当前实例锁由于SynchronizedBlocked自身也是线程启动后在其run方法中也调用了f()但由于对象锁被其他线程占用导致t线程只能等到锁此时我们调用了t.interrupt();但并不能中断线程。 等待唤醒机制与synchronized notify/notifyAll和wait方法在使用这3个方法时必须处于synchronized代码块或者synchronized方法中否则就会抛出IllegalMonitorStateException异常这是因为调用这几个方法前必须拿到当前对象的监视器monitor对象 synchronized (obj) {obj.wait();obj.notify();obj.notifyAll(); }与sleep方法不同的是wait方法调用完成后线程将被暂停**但wait方法将会释放当前持有的监视器锁(monitor)直到有线程调用notify/notifyAll方法后方能继续执行而sleep方法只让线程休眠并不释放锁。**同时notify/notifyAll方法调用后并不会马上释放监视器锁而是在相应的synchronized(){}/synchronized方法执行结束后才自动释放锁。 更多Android相关文章 Android如何进阶http://docs.qq.com/doc/DWHFqVHBMVEJPWUx Android面试题汇总http://docs.qq.com/doc/DWGZIRFh5VEtYWE1D Android音视频需要学习哪些http://docs.qq.com/doc/DWFFWZHNPTHZVdHFX Android常有的开源框架有哪些框docs.qq.com/doc/DWHlGYUdseVhsSUda Android车载应需要学习哪些docs.qq.com/doc/DWEl0blBabXVvU2Nw Android Framework怎么学docs.qq.com/doc/DWFdlc2JocEtNbEJ1
http://www.ho-use.cn/article/10820616.html

相关文章:

  • 网站开发 加密保护《电子商务网站建设 》
  • 做网站的公司叫什么软件wordpress enter
  • 东莞响应式网站制作个人建站的app哪里有卖
  • 深圳机械网站建设wordpress邮箱注册功能
  • 微信公众号的微网站开发html网页制作接单
  • 网站推广优化平台平度建设局网站
  • 商丘企业网站建设团队博客做公司网站
  • 广东500强企业名单一览表公司优化网站的案例
  • 上海高端网站定制网站建设开发岗位职责
  • 注册百度网站怎么弄成都响应式网站建
  • 网站建设公司有哪些原青岛中小企业网站制作
  • 网站的建设可以起到什么作用wordpress站点标题看不到
  • 阿里巴巴申请网站怎么做wordpress绝对链接转相对
  • 做网站准备wordpress百度云下载文件
  • 公司如何做网站搭建企业交流平台
  • 东莞专业网站设计专业服务站长之家app
  • 交易网站备案科技软件下载
  • 山东建设厅官方网站二建报名网站建设招标公示
  • 宝安多屏网站建设公司好吗新手学做网站需要注意的几点
  • 2017网站趋势训做网站的心得体会范文
  • 帮人做网站的公司泰安房产网站
  • 京东网站开发技术工程项目外包平台
  • 设计师门户网站源码网站营销工作流程
  • 电脑怎么做服务器 网站整合营销传播最基础的形式是
  • oss做网站迁移电子商务网站运营与管理
  • 泰安住房和城乡建设局网站wordpress load-scripts.php
  • 建立公司网站的流程开发公司总经理竞聘报告
  • 企业展厅装修福州短视频seo费用
  • 通化工程建设信息网站wordpress自动安装
  • 青岛门户网站建设电子商务网站建设背景