Rumah >Java >javaTutorial >Status benang, isu keselamatan benang dan penggunaan kata kunci disegerakkan dalam Java
Pada peringkat sistem pengendalian, benang mempunyai dua keadaan: sedia dan disekat.
Tetapi mengikut susunan untuk mengetahui dengan cepat sebab sekatan utas apabila utas disekat, Java telah memperhalusi lagi status sekatan. telah dibuat, tetapi utas peringkat sistem belum dicipta, atau objek utas belum dipanggil start()
DITAMATKAN: Benang dalam sistem telah dimusnahkan, tetapi utas objek dalam kod masih ada, iaitu, selepas run() selesai berjalan, objek Thread masihpublic class Demo1 { private static int count=0; public static void main(String[] args) { Thread t1=new Thread(()->{ for(int i=0;i<50000;i++){ count++; } }); t1.start(); Thread t2=new Thread(()->{ t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(count); } } //打印结果:68994
Jelas sekali hasil yang dijangkakan ialah 100,000, tetapi dikira Ia lebih daripada 6w, yang merupakan masalah keselamatan benang
:
Pemerhatian ditemui: dua Setiap utas dilaksanakan kiraan++ sekali, tetapi keputusan kedua-dua ++ tidak memuaskan, iaitu bersamaan dengan hanya satu kenaikan automatik masalah keselamatan benang.
Dua gambar di atas menunjukkan situasi di mana masalah keselamatan benang berlaku Hasilnya ialah dua penambahan digunakan sebagai satu Jika dua utas A telah berada dalam keadaan ini (juga keadaan terburuk), tetapi hasil pengiraan ialah 5w Jika dua utas telah dilaksanakan sepenuhnya load-add-save thread lain akan melaksanakannya semula. Operasi sedemikian akan dilaksanakan secara bersiri, yang bukan 10w.
3. Bagaimana untuk menyelesaikan kes di atas?Kes itu juga disebut di hujungnya sebagai selagi serialisasi boleh dicapai Perlaksanaan dengan cara ini dapat memastikan ketepatan keputusan Maka Java memang mempunyai fungsi sedemikian untuk kita gunakan, iaitu penggunaan kata kunci yang disegerakkan 🎜>
Dalam erti kata lain: pelaksanaan cpu1 Objek kunci dikunci sebelum dimuatkan, dan kemudian dibuka kunci selepas menyimpan Hanya kemudian cpu2 boleh mengunci objek dan melakukan satu siri operasi pada masa ini, keatoman tambah-beban. save dijamin. Supaya ketiga-tiga langkah ini sama ada tidak dilaksanakan, dan perlaksanaan selesai sekali sahajaKemudian anda boleh bertanya, apakah perbezaan antara ini dan hanya menggunakan satu utas utama untuk. kirakan kenaikan automatik 100,000 kali ganda. Apa gunanya mencipta berbilang urutan?
意义很大,因为我们创建的线程很多时候不仅仅只是一个操作,光针对自增我们可以通过加锁防止出现线程安全问题,但是各线程的其他操作要是不涉及线程安全问题那就可以并发了呀,那此时不就大大提升了执行效率咯.
4.具体如何加锁呢?
此处先只说一种加锁方式,先把上述案例的问题给解决了再说.
使用关键字synchronized,此处使用的是给普通方法加synchronized修饰的方法(除此之外,synchronized还可以修饰代码块和静态方法)
class Counter{ private int count; synchronized public void increase(){ this.count++; } public int getCount(){ return this.count; } } public class Demo2 { private static int num=50000; public static void main(String[] args) { Counter counter=new Counter();//此时对象中的count值默认就是0 Thread t1=new Thread(()->{ for (int i = 0; i < num; i++) { counter.increase(); } }); t1.start(); Thread t2=new Thread(()->{ for (int i = 0; i < num; i++) { counter.increase(); } }); t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(counter.getCount()); } }//打印10W
首先说明:这是有编译器优化导致的,其次要知道cpu读取变量时:先从主内存将变量的值存至缓存或者寄存器中,cpu计算时再在寄存器中读取这个值.
当某线程频繁的从内存中读取一个不变的变量时,编译器将会把从内存获取变量的值直接优化成从寄存器直接获取.之所以这样优化,是因为,cpu从主内存中读取一个变量比在缓存或者寄存器中读取一个变量的值慢成千上万倍,如果每每在内存中读到的都是同一个值,既然缓存里头已经有这个值了,干嘛还大费周折再去主内存中进行获取呢,直接从缓存中直接读取就可以了,可提升效率.
但是:一旦一个线程被优化成上述的情况,那如果有另一个线程把内存中的值修改了,我被优化的线程还傻乎乎的手里拿着修改之前的值呢,或者内存中的变量值被修改了,被优化的线程此时已经感应不到了.
具体而言:
public class Demo3 { private static boolean flag=false; public static void main(String[] args) { Thread t1=new Thread(()->{ while(!flag){ System.out.println("我是优化完之后直接读取寄存器中的变量值才打印的哦!"); } }); t1.start(); flag=true; System.out.println("我已经在主线程中修改了标志位"); } }
运行上述代码之后,程序并不会终止,而是一直在那打印t1线程中的打印语句.
如何解决上述问题:
引入关键字volatile:防止内存可见性问题,修饰一个变量,那某线程想获取该变量的值的时候,只能去主内存中获取,其次它还可以防止指令重排序,指令重排问题会在线程安全的单例模式(懒汉)进行介绍.具体:
public class Demo3 { private static volatile boolean flag=false; public static void main(String[] args) { Thread t1=new Thread(()->{ while(!flag){ System.out.println("我是优化完之后直接读取寄存器中的变量值才打印的哦!"); } }); t1.start(); try { Thread.sleep(1);//主线程给t1留有充足的时间先跑起来 } catch (InterruptedException e) { e.printStackTrace(); } flag=true; System.out.println("我已经在主线程中修改了标志位"); } } //打印若干t1中的打印语句之后,主线程main中修改标志位之后,可以终止t1
注意:上述优化现象只会出现在频繁读的情况,如果不是频繁读,就不会出现那样的优化.
生活案例:买菜
如果是傻乎乎的按照菜单从上到下的去买菜,从路线图可以看出,不必要的路是真的没少走.
如果执行代码时,编译器认为某些个代码调整一下顺序并不会影响结果,那代码的执行顺序就会被调整,就比如可以把上面买菜的顺序调整成:黄瓜->萝卜->青菜->茄子
单线程这样的指令重排一般不会出现问题,但是多线程并发时,还这样优化,就容易出现问题
针对这样的问题,如果是针对一个变量,我们可以使用volatile修饰,如果是针对代码块,我们可以使用synchronized.
synchronized起作用的本质
修饰普通方法
修饰静态方法
修饰代码块
因为我们知道java中所有类都继承了Object,所以所有类都包含了Object的部分,我们可以称这继承的部分是"对象头",使用synchronized进行对象头中的标志位的修改,就可以做到一个对象的锁一个时刻只能被一个线程所持有,其他线程此时不可抢占.这样的设置,就好像把一个对象给锁住了一样.
如前述两个线程给同一个count进行自增的案例.不再赘述.此时的所对象就是Counter对象
与普通方法类似.只不过这个方法可以类名直接调用.
首先修饰代码块需要执行锁对象是谁,所以这里可以分为三类,一个是修饰普通方法的方法体这个代码块的写法,其次是修饰静态方法方法体的写法,最后可以单独写一个Object的对象,来对这个Object对象进行上锁.
class Counter{ private int count; public void increase(){ synchronized(this){ count++; } } public int getCount(){ return this.count; } }
class Counter{ private static int count; public static void increase(){ synchronized(Counter.class){//注意这里锁的是类对象哦 count++; } } public int getCount(){ return this.count; } }
class Counter{ private static int count; private static Object locker=new Object(); public static void increase(){ synchronized(locker){ count++; } } public int getCount(){ return this.count; } }
注意:java中这种随手拿一个对象就能上锁的用法,是java中一种很有特色的用法,在别的语言中,都是有专门的锁对象的.
java中的线程状态,以及如何区分线程安全问题 罪恶之源是抢占式执行多线程对同一个变量进行修改,多线程只读一个变量是没有线程安全问题的修改操作是非原子性的内存可见性引起的线程安全问题指令重排序引起的线程安全问题 synchronized的本质和用法
1.java中的线程状态,以及如何区分
2.线程安全问题
罪恶之源是抢占式执行
Jika berbilang benang mengubah suai pembolehubah yang sama, tiada isu keselamatan benang jika berbilang benang hanya membaca satu pembolehubah
Operasi pengubahsuaian bukan atom
Isu keselamatan benang yang disebabkan oleh keterlihatan ingatan
Isu keselamatan benang yang disebabkan oleh penyusunan semula arahan
3 .Intipati dan penggunaan disegerakkan
Atas ialah kandungan terperinci Status benang, isu keselamatan benang dan penggunaan kata kunci disegerakkan dalam Java. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!