java 多线程(4) 线程同步之锁(synchronized) / 死锁 / 两个锁定对象期间访问(修改)其变量的面试
来源:互联网 发布:mac cocos2dx android 编辑:程序博客网 时间:2024/05/20 19:32
一. 锁的定义
锁就是synchronized 关键字,记住synchronized(this )是锁定当前对象。在函数m1()里面写synchronized( this ),这个和public synchronized void m1() 等价。
但是他只锁定当前对象的synchronized 大括号内的话,其他,这个synchronized 不去锁定。这个对象的其他方法 / 变量( 被synchronized 语句块改过后的) 还能被其他线程调用。锁定了这个对象,但是不是说完全锁定了,针对这个方法的锁定期间,其他方法照样可以修改变量值。 所以,如果你想把类变量 ( 银行账户值 ) 值控制的完全,不会被错误修改,要把所有控制这个变量的方法全部考虑到,该synchronized 要synchronized 。
二. 锁的粒度
如果是对象变量,锁this,即只对操作这个对象的众多线程有限制作用,对其他类对象无关,如果是类变量,锁class。锁谁,就是拿到谁的锁,那么其他需要这个锁的语句块,就得排队,但是不需要这个锁的语句块,直接执行不受阻碍。所以就有了锁obja,锁objb这种有多把锁的情况。而不是简单的只锁this 。也就是说m1() 要等着锁的到来才能执行,而m2()不需要等着锁就能执行( synchronized )。
三. 锁的代码
锁代码1:
package test.java.Thread;public class TestSync implements Runnable{Timer timer = new Timer();public static void main(String[] args) {TestSync test = new TestSync();Thread t1 = new Thread(test);Thread t2 = new Thread(test);//注意这种用法,Runnable() 参数不为空,则调用t1.start() 时候,会调用test 的run()方法t1.setName("t1");t2.setName("t2");t1.start();t2.start();}@Overridepublic void run() {timer.add(Thread.currentThread().getName());}}class Timer {private int num =0;public void add(String name){synchronized (this) { //很明显不加这一句,打印结果是错的,解决办法就是在执行这7句话的过程中,请你把我当前的对象锁住 //这里加this 叫锁定当前对象num++; //锁定当前对象的意思是在执行这个大括号里面的语句之中,一个线程执行的过程之中,不会被另外一个线程打断,一个线程已经进入到try{ //我这个锁定的区域里面了,你放心,不可能有另外一个线程也在这里面,这叫互斥锁。Thread.sleep(1);}catch(InterruptedException e){}System.out.println(name+" 你是第"+num+"个使用timer 的线程");//t1 你是第2个使用timer 的线程 t2 你是第2个使用timer 的线程 }//end synchronized}//public synchronized void add(String name){//这个是上面的简便写法,意思是在执行这个函数的过程之中,锁定当前对象//.//.//.//}}
死锁代码2
package test.java.Thread;/** * * @author jalo *这是两个线程死锁,哲学家问题是多个线程转着圈的死锁 * 解决死锁的办法之一,把锁的粒度加粗一些,你锁定一个对象不就行了?非要锁定里面的两个对象(o1,o2), * 当然解决死锁还有很多其他办法。如果不写系统级的程序,很难碰到死锁的问题。因为死锁都被中间件厂商给解决了。 * 如果有机会写系统级的程序,比如自己实现一个数据库的连接池,就会自己实现各种锁,就会去考虑控制死锁。 * */public class TestDeadLock implements Runnable{ int flag ;static Object o1 = new Object();static Object o2 = new Object();@Overridepublic void run() {if(flag==0){synchronized (o1) {try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}synchronized (o2) {System.out.println("flag==0");}}}else if (flag==1){synchronized (o2) {try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}synchronized (o1) {System.out.println("flag==1");}}}}public static void main(String[] args) {TestDeadLock td1 = new TestDeadLock();TestDeadLock td2 = new TestDeadLock();td1.flag = 0;td2.flag = 1;Thread t1 = new Thread(td1);Thread t2 = new Thread(td2);t1.start();t2.start();}}
锁定对象期间访问其对象的面试题,代码3
package test.java.Thread;/** * * @author jalo * 这是个面试题,一个线程在调用m1()期间,另一个线程可以调用m2()吗?如果可以,那么m2()调用的结果是b=0还是b=1000? * 可以调用,b=1000 * synchronized(this)是锁定当前对象,但是他只锁定当前对象的synchronized 大括号内的话, * 这个对象的其他方法 / 变量,这个synchronized 不去锁定。这个对象的其他方法 / 变量( 被synchronized 语句块改过后的) 还能被其他线程调用。 */public class ThreadLockQuestion {int b = 0;public synchronized void m1() throws Exception{b = 1000;Thread.sleep(1000000000);System.out.println("b= "+b);}public void m2(){System.out.println(b);}public static void main(String[] args) {ThreadLockQuestion tlq = new ThreadLockQuestion();ThreadLock tl1 = new ThreadLock(tlq);ThreadLock tl2 = new ThreadLock(tlq);tl1.flag = 1;tl2.flag = 2;Thread t1 = new Thread(tl1);Thread t2 = new Thread(tl2);t1.start();t2.start();}}class ThreadLock implements Runnable{ThreadLockQuestion tlq ;int flag;ThreadLock(ThreadLockQuestion tlq){this.tlq = tlq;}@Overridepublic void run() {try {if(flag==1){tlq.m1();System.out.println("flag==1");}if(flag==2){Thread.sleep(1000);tlq.m2(); //这里m2()看到的是1000 , System.out.println("flag==2");}} catch (Exception e) {e.printStackTrace();}}}结果:
1000 //说明是m2()的调用结果,而且b 已经被锁语句块给改成了1000,并且锁住对象期间,还能访问这个对象的变量,访问结果是最新值
flag==2
............ //一大堆时间过后
b=1000
flag==1
锁定对象期间修改其变量的面试题,代码4
package test.java.Thread;/** * * @author jalo * 这是个面试题,一个线程在调用m1()期间,另一个线程可以调用m2()吗?如果可以,那么m2()调用的结果是b=0还是b=1000? * 可以调用,b=1000 * synchronized(this)是锁定当前对象,但是他只锁定当前对象的synchronized 大括号内的话,即m1()方法中的语句 * 锁定了这个对象,但是不是说完全锁定了这个对象,针对这个方法m1()的锁定期间,m2()方法照样可以修改变量b 的值。 * */public class ThreadLockQuestion {int b = 0;public synchronized void m1() throws Exception{b = 1000;Thread.sleep(5000);System.out.println("b= "+b); //这个是b=2000,意思是虽然m1()被锁定期间,但是m2()照样能改b 的值}public void m2() throws Exception{Thread.sleep(2500);b=2000;System.out.println(b);}public static void main(String[] args) {ThreadLockQuestion tlq = new ThreadLockQuestion();ThreadLock tl1 = new ThreadLock(tlq);ThreadLock tl2 = new ThreadLock(tlq);tl1.flag = 1;tl2.flag = 2;Thread t1 = new Thread(tl1);Thread t2 = new Thread(tl2);t1.start();t2.start();}}class ThreadLock implements Runnable{ThreadLockQuestion tlq ;int flag;ThreadLock(ThreadLockQuestion tlq){this.tlq = tlq;}@Overridepublic void run() {try {if(flag==1){tlq.m1();System.out.println("flag==1");}if(flag==2){Thread.sleep(1000);tlq.m2(); //这里m2()看到的是1000 , System.out.println("flag==2");}} catch (Exception e) {e.printStackTrace();}}}
结果:
2000 //以下是m2()调用结果
flag==2
b= 2000 // 以下是m1()调用结果,可见m1()锁定期间,m2()照样能改b 的值。使用同步要非常小心,你m1()同步了,但是其他线程可以自由访问任意未同步的方法m2(),其访问可以对你同步的方法m1()产生影响。所以要仔细考虑函数加不加同步,加了同步,导致效率变低,不加同步,有可能导致数据不一致的情况。
flag==1 //
代码4改进,代码5
如果你想把类变量 ( 银行账户值 ) 值控制的完全,不会被错误修改,要把所有控制这个变量的方法全部考虑到,该synchronized 要synchronized。这里把m2()也改成synchronized 即可。
改了这一句,加了synchronized,public synchronized void m2() throws Exception{
package test.java.Thread;/** * * @author jalo * 这是个面试题,一个线程在调用m1()期间,另一个线程可以调用m2()吗?如果可以,那么m2()调用的结果是b=0还是b=1000? * 可以调用,b=1000 * synchronized(this)是锁定当前对象,但是他只锁定当前对象的synchronized 大括号内的话,即m1()方法中的语句 * 锁定了这个对象,但是不是说完全锁定了这个对象,针对这个方法m1()的锁定期间,m2()方法照样可以修改变量b 的值。 * */public class ThreadLockQuestion {int b = 0;public synchronized void m1() throws Exception{b = 1000;Thread.sleep(5000);System.out.println("b= "+b); //这个是b=2000,意思是虽然m1()被锁定期间,但是m2()照样能改b 的值}public synchronized void m2() throws Exception{Thread.sleep(2500);b=2000;System.out.println(b);}public static void main(String[] args) {ThreadLockQuestion tlq = new ThreadLockQuestion();ThreadLock tl1 = new ThreadLock(tlq);ThreadLock tl2 = new ThreadLock(tlq);tl1.flag = 1;tl2.flag = 2;Thread t1 = new Thread(tl1);Thread t2 = new Thread(tl2);t1.start();t2.start();}}class ThreadLock implements Runnable{ThreadLockQuestion tlq ;int flag;ThreadLock(ThreadLockQuestion tlq){this.tlq = tlq;}@Overridepublic void run() {try {if(flag==1){tlq.m1();System.out.println("flag==1");}if(flag==2){Thread.sleep(1000);tlq.m2(); //这里m2()看到的是1000 , System.out.println("flag==2");}} catch (Exception e) {e.printStackTrace();}}}
结果:
b= 1000
flag==1 //由此可见,本来应该m2()调用先出结果,但是却死等m1()结束,说明synchronized m2() 起了作用。
2000
flag==2 //线程1,在调用 m1() 的执行的过程中,线程2要调用其他的 synchronized 的函数也会卡死,直到线程1执行完,排队的线程2 的函数再执行。
//于是,m1(),m2() 这种都是synchronized 的函数,又一样了,又有顺序了,又不能打乱了,无论几个线程执行他们,都是按照顺序。谁先调用(谁
先拿到this 锁,这里是t1 先拿到),谁就执行,执行完,再执行后排队的,一个个调用着走。这其实就是第一行说的和synchronized( this ) 等价,故
t1 调用 m1() 期间拿到this的锁,直到 t1 结束,t2 调用m2() 才能拿到this 的锁去执行m2()。这叫从原理上理解。
- java 多线程(4) 线程同步之锁(synchronized) / 死锁 / 两个锁定对象期间访问(修改)其变量的面试
- java多线程之-----对象及变量的并发访问1(synchronized同步方法)
- Java多线程之线程同步和死锁
- java多线程之synchronized(线程同步)
- Java多线程-3 线程同步之synchronized
- Java多线程,线程同步synchronized,线程死锁【线程池常规用法】多线程并发处理
- Java多线程编程2--同步锁定--死锁
- Java 线程的同步(synchronized)与死锁
- Java多线程之同步锁定--synchronized同步方法和同步块、脏读、锁重入、死琐
- 线程的同步与锁定-synchronized初解
- 多线程之synchronized(this)锁定当前对象
- java线程安全篇之synchronized对象锁的同步和异步(三)
- java多线程-线程池-synchronized-死锁
- 第二章、对象及变量的并发访问 之 synchronized 同步方法(1)
- 第二章、对象及变量的并发访问 之 synchronized 同步方法(2)
- synchronized关键字修饰对象锁,同步与非同步对该方法的访问及修改
- Java多线程编程2--同步锁定--synchronized同步块
- java多线程编程之使用Synchronized块同步变量
- Android中取消标题栏遇到的问题
- [从头学数学] 第41节 万以内的加法和减法(二)
- 记得花时间汇总一下,Android MakeFile 写法
- 九宫格的算法
- 树学习关键点
- java 多线程(4) 线程同步之锁(synchronized) / 死锁 / 两个锁定对象期间访问(修改)其变量的面试
- nanomsg源码阅读
- windows本机ssh连接ubuntu
- hadoop2解决 NameNode 单点故障问题的 高可用集群配置
- springmvc拦截器
- HDOJ 1575 Tr A (矩阵快速幂)
- MYSQL存储过程
- LAME库裁剪
- [工作小结]PHP和其他编程语言联合开发网站的一种方法