选调生面试,给我一个黄网站,jianghuyinniang
本篇博文作为多线程技术的读书笔记,主要学习了以下知识点:
“非线程安全”问题存在于“实例变量”中,如果是方法内部的私有变量,则不存在“非线程安全”问题,这是方法内部的变量都是线程私有的特性造成的。
如果多个线程共同访问一个对象中的实例变量,则有可能出现“非线程安全”问题。
关键字synchronized拥有锁重入功能,即当使用synchronized时,当一个线程得到一个对象锁后,再次请求此对象锁时是可以再次得到该对象的锁的,这也证明在一个synchronized方法/块的内部调用本来的其他synchronized方法/块时,是永远可以得到锁的。
可重入锁也同样适用于父子集成关系中,子类是完全可以通过“可重入锁”调用父类的同步方法的。
当一个线程执行的代码出现异常时,其所持有的锁会自动释放。
如果父类某个方法是同步的,子类某个方法是非同步的,子类的非同步方法调用了父类中的同步方法,在多线程环境下调用子类的该同步方法,是无法同步的,必须子类的这个方法也实现同步。
用关键字synchronized声明方法在某些情况下是有弊端的,比如线程a调用同步方法执行一个长时间的任务,那么线程b线程则必须等待比较长时间的,即这个方法锁住的资源太多,锁粒度太大,不利于业务高效运行,这个时候我们就可以锁同步块。
在使用同步synchronized(this)代码块时需要注意的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对同一个object中所有其他synchronzied(this)同步代码块的访问将被阻塞,这说明此时synchronized使用的是同一个对象监视器(同一个对象锁)。
synchronized(this)同步方法持有的是对象锁。
多个线程调用同一个对象中的不同名称的synchronized同步方法或者synchronized(this)同步代码块时,调用的效果就是按顺序执行,也就是同步的,阻塞的。
在多个线程持有的对象监视器为同一个对象的前提下,同一时间只哟局一个线程可以执行synchronized(对象)同步方法/代码块中的内容。
锁非this对象具有一定的优先:如果在一个类中有很多个synchronized方法,这时虽然能实现同步,但会收到阻塞,所以影响运行效率;但如果使用代码块锁非this对象,则synchronized(对象)代码块中的程序与同步方法是异步的,不予其他锁this同步方法争抢this锁,则可以大大提高运行效率。
关键字synchronized应用在static静态方法上,那就是对当前*.java文件对应的class类进行持锁。
synchronized public static void pringa(){}
synchronized关键字加到static静态方法上是给class类上锁,而synchronized关键字加到非static静态方法上是给对象上锁。
在jvm中具有string常量池缓存的功能,比如以下:
1 string a = "a"; 2 string b = "a"; 3 system.out.println(a == b); 4 // true
即两个不同的string对象当时如果值相同,两个对象也是相等的,这就是string的常量池缓存特性。
当我们的synchronized(string)同步块与string联合使用时,就可能会应为这个常量池特性而带来不受控的一些影响了。因为如果两个不相干的线程在调用此次方法时,使用了相同的string值,那么原本不希望阻塞的异步方法此时也阻塞了。
1 public static void print(string str) { 2 synchronized (str) { 3 system.out.println(thread.currentthread().getname()); 4 } 5 }
因此大多数情况下,同步synchronized代码块都不使用string作为锁对象,而改用其他如object对象。
多线程的锁使用不当,很容易引起线程间的死锁,下面通过一个简单的例子来说明死锁。
1 public class mythread09 extends thread { 2 3 public string flag; 4 public object lock1 = null; 5 public object lock2 = null; 6 7 public mythread09(string flag, object lock1, object lock2) { 8 this.flag = flag; 9 this.lock1 = lock1; 10 this.lock2 = lock2; 11 } 12 13 @override 14 public void run() { 15 if(flag.equals("a")) { 16 synchronized (lock1) { 17 system.out.println("=== print a"); 18 try { 19 thread.sleep(3000l); 20 } catch (interruptedexception e) { 21 e.printstacktrace(); 22 } 23 synchronized (lock2) { 24 system.out.println("=== print a again"); 25 } 26 } 27 } 28 if(flag.equals("b")) { 29 synchronized (lock2) { 30 system.out.println("=== print b"); 31 try { 32 thread.sleep(5000l); 33 } catch (interruptedexception e) { 34 e.printstacktrace(); 35 } 36 synchronized (lock1) { 37 system.out.println("=== print b again"); 38 } 39 } 40 } 41 } 42 43 public static void main(string[] args) { 44 // lock1,lock2作为锁,或者说临界区资源 45 object lock1 = new object(); 46 object lock2 = new object(); 47 thread threada = new mythread09("a", lock1, lock2); 48 thread threadb = new mythread09("b", lock1, lock2); 49 threada.start(); 50 threadb.start(); 51 } 52 } 53 54 运行结果: 55 === print a 56 === print b 57 (程序无法停止)
当程序正在运行时,我们打开jdk自带的jps.exe来查看当前正在运行程序的id值。
ps c:\program files\java\jdk1.8.0_131\bin> .\jps.exe 25856 mythread09 29152 appliaction 34688 jps 33972 launcher 35780 launcher
然后再执行jstack命令,查看具体的堆栈细节。
found one java-level deadlock: ============================= "thread-1": waiting to lock monitor 0x000000001c30dc48 (object 0x000000076b39f1a8, a java.lang.object), which is held by "thread-0" "thread-0": waiting to lock monitor 0x000000001c310638 (object 0x000000076b39f1b8, a java.lang.object), which is held by "thread-1" java stack information for the threads listed above: =================================================== "thread-1": at com.captainad.mythread09.run(mythread09.java:46) - waiting to lock <0x000000076b39f1a8> (a java.lang.object) - locked <0x000000076b39f1b8> (a java.lang.object) "thread-0": at com.captainad.mythread09.run(mythread09.java:33) - waiting to lock <0x000000076b39f1b8> (a java.lang.object) - locked <0x000000076b39f1a8> (a java.lang.object) found 1 deadlock.
从堆栈中可以看到程序已经发生了死锁,两个线程在持有不同的锁的情况下,还继续等待着获取对方持有的还没有释放的锁,形成了相互等待,最终造成程序死锁。
如果锁是string类型的,这个类型值后续改变了,锁也会随之改变,即争夺的资源就改变了。
如果锁的是对象,对象的属性在某个线程中改变了,线程间相互竞争的锁还是同一个对象的,不会因为对象的值改变而变更。
关键字volatile的主要作用是使变量的变更在多线程间可见。
通过使用volatile关键字,强制线程从公共内存中读取变量的值,volatile关键字保证了变量在多线程间的可见性,但是该关键字不支持原子性。
下面将关键字synchronized和volatile做一个简单的比较:
(java多线程编程核心技术 截图)
1 volatile int i = 0; 2 thread: i++;
在多线程环境下,即使给变量i加了volatile修饰,也是无法保证其计算的原子性的,上面的截图给出了解释,那么要做到一个多线程情况下使用的计数器该如何处理呢?
多线程的使用会产生线程安全问题,处理好线程安全问题也是我们学习多线程知识中需要掌握的一个技能。
关键字synchronized在处理同步块时,可能会出现多种情况,下面做个简单总结:
如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复
浅析我对 String、StringBuilder、StringBuffer 的理解
使用IDEA搭建SSM框架的详细教程(spring + springMVC +MyBatis)
Springboot整合freemarker 404问题解决方案
引入mybatis-plus报 Invalid bound statement错误问题的解决方法
网友评论