当前位置: 首页 > 科技观察

线程安全之Synchronized关键字

时间:2023-03-22 14:37:34 科技观察

之前讲过线程的基础知识,本文将带大家学习线程安全相关的知识。1多线程下为什么会存在线程安全问题合理使用线程可以提高程序的处理性能。首先,可以使用多核CPU来实现线程的并行执行。其次,线程的异步执行可以提高系统吞吐量。线程虽然有这些优点,但是也带来了很多问题。例如:1.1共享变量带来的安全问题先看一张图:一个变量i,如果线程A或者线程B单独访问和修改变量i的值没有问题,那么如果并行修改变量i,那么就会有安全问题。然后用代码模拟这个场景。为了更好的看到效果,我使用了100个线程:publicclassThreadDemo1{privatestaticinti=0;publicstaticvoidinc(){try{Thread.sleep(1);}catch(InterruptedExceptione){e.printStackTrace();}i++;}publicstaticvoidmain(String[]args)throwsInterruptedException{for(inti=0;i<100;i++){newThread(()->ThreadDemo1.inc()).start();}Thread.sleep(1000);System.out。println("运算结果"+i);}}输出结果:88这个输出结果不固定,第一次可能是88,第二次可能是87,这个结果和我们预期的结果不一致(预期结果是100),所以一个对象是否线程安全取决于它是否会被多线程访问以及该对象在程序中的使用方式。如果多个线程访问同一个共享对象,共享对象的状态仍然是正确的(正确意味着这个对象的结果和我们预期的结果是一致的),也就是说这个对象是线程安全的。为了线程安全,它本质上管理对数据状态的访问,而这种状态通常是共享和可变的。Shared:表示这个数据变量可以被多个线程访问;variable:表示这个变量的值在它的生命周期内是可以改变的。2、如何保证线程并行-Synchroinzed的数据安全针对以上情况,我们如何解决这个问题呢?首先想到的就是加一把锁,而这个锁必须是互斥的。比如上图的例子,如果线程A正在修改i的值,那么线程B就不能修改i的值。也就是说,并行修改共享变量的值会有线程安全问题,所以我们不让你并行,就解决了这个问题。所以java提供了Synchroinzed关键字。2.1Synchroinzed的基本认识Synchroinzed由来已久,不过之前是重量级的锁,所以非常好用。在javaSE1.6中优化了Synchroinzed,引入了偏向锁和轻量级锁。所以在并发量不高的时候推荐使用Synchroinzed加锁。为什么在并发不高的时候推荐使用,因为并发高的时候Synchroinzed会升级为重量级锁。2.2Synchroinzed的三种加锁方式修改实例方法。锁是当前实例对象。在进入同步代码之前,必须先获取当前实例的锁,才能修改静态方法。锁是当前类的类对象。在进入同步代码之前,必须先获取当前类对象。锁修改代码块。锁是括号中的对象。它锁定给定的对象。在进入同步代码库之前,必须获得给定对象的锁。看简单代码publicclassSynchroinzedDemo{/***锁定静态方法*/publicstaticsynchronizedvoidtest(){}/***锁定实例方法*/publicsynchronizedvoidtest1(){}/***锁定代码块*/publicvoidtest2(){synchronized(this){}}}然后我们实现上面的例子进行同步加锁:publicclassThreadDemo1{privatestaticinti=0;publicstaticvoidinc(){synchronized(ThreadDemo1.class){try{Thread.sleep(1);}catch(InterruptedExceptione){e.printStackTrace();}i++;}}publicstaticvoidmain(String[]args)throwsInterruptedException{for(inti=0;i<100;i++){newThread(()->ThreadDemo1.inc())。start();}Thread.sleep(1000);System.out.println("运行结果"+i);}}运行结果:运行结果100完美解决共享变量并行修改带来的线程安全问题。3小结本文带大家了解线程的安全问题以及synchronized关键字解决线程安全问题的用法。下面的并发编程系列会讲解更多解决线程安全的方法。敬请关注!