多线程 synchronized,synchronized同步方法和同步代码块
00-1010 1.同步方法和同步块哪个好?2.同步的同步代码块3。如果同步块中的线程抛出异常,会发生什么?总结面试题:
1同步方法和同步块,哪个更好?
2.如果同步块中的线程抛出异常,会发生什么?
00-1010同步块更好,这意味着同步块之外的代码异步执行,比同步整个方法更能提高代码的效率。请知道一个原则:同步的范围越小越好。
对于小的关键区域,我们可以直接在方法声明中设置synchronized同步关键字,这样可以避免竞争条件。但对于大的临界段代码段,为了执行效率,最好将同步方法分成小的临界段代码段。
public class two plus { private int num 1=0;private int num 2=0;public synchronized void plus(int val 1,int val 2){ this . num 1=num 1 val 1;this . num 2=num 2 val 2;}}临界区代码段包含对两个临界区资源的操作,分别是sum1和sum2。同步保护plus(int val1,int val2)后,进入临界段代码段的线程拥有sum1和sum2的操作权限,并且都被占用。一旦线程进入,当线程在操作sum1而不是sum2时,sum2的操作权也会被白白占用。其他线程只能看sum2空闲,不能执行操作,因为没有进入临界区。
因此,在方法中加入synchronized方法,如果受保护的临界段代码段包含多个临界段资源,就会造成临界段资源的空闲等待,进而影响临界段代码段的吞吐量。为了提高吞吐量,可以将synchronized关键字放在函数体中,同步一个代码块。同步的同步块写如下:
Synchronized (syncObject){ //临界段代码段的代码块}是syncObject对象,在Synchronized同步块后面的括号中,表示进入临界段代码段时需要获得syncObject对象的监控锁。因为每个Java对象都有一个监控锁,所以任何Java对象都可以用作同步的同步锁。
使用synchronized同步块来提高上述TwoPlus类的吞吐量。具体代码如下:
public class two plus { private int num 1=0;private int num 2=0;//两个不同的锁对象私有对象Object 1=new Object();私有对象Object 2=new Object();public void plus(int val1,int val 2){ synchronized(object 1){ this . num 1=num 1 val 1;}同步(object 2){ this . num 2=num 2 val 2;}}}转换后,可以同时执行两个独立的关键区域资源sum1和sum2的添加。在某个时刻,不同的线程可以同时相加sum1和sum2,提高了plus()方法的吞吐量。
同步方法和同步同步块有什么区别?
一般来说,synchronized方法是一种粗粒度的并发控制,一次只能有一个线程执行synchronized方法。synchronized代码块是一个细粒度的并发控制,synchronized代码块之外的其他代码可以被多个线程并发访问。在一个方法中,并不是所有的代码都是临界区,线程同步中可能只涉及几行代码。因此,与synchronized方法相比,synchronized代码块可以更好地控制多线程的同步访问。
synchronized方法的同步锁本质上使用这个对象锁,因此不需要手动设置同步锁。但是,使用同步代码块需要手动设置同步锁。
目录
公共类room demo { private static int count=0;//创建一个锁对象,同步代码块需要手动设置对象锁。
private static Object object = new Object(); public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(()->{ for(int i=0;i<5000;i++){ // 使用object对象锁住临界区资源 synchronized (object){ count++; } } },"t1"); Thread t2 = new Thread(()->{ // 使用object对象锁住临界区资源 for(int i=0;i<5000;i++){ synchronized (object){ count--; } } },"t2"); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(count);// 0 }}
你可以做这样的类比: synchronized(对象) 中的对象,可以想象为一个房间,线程 t1,t2 想象成两个人
(1) 当线程 t1 执行到 synchronized(object) 时就好比 t1 进入了这个房间,并锁住了门拿走了钥匙,在门内执行 count++ 代码 ;
(2) 这时候如果 t2 也运行到了 synchronized(object) 时,它发现门被锁住了,只能在门外等待,发生了上下文切换,阻塞住了 ;
(3) 这中间即使 t1 的 cpu 时间片不幸用完,被踢出了门外 (不要错误理解为锁住了对象就能一直执行下去哦) , 这时门还是锁住的,t1 仍拿着钥匙,t2 线程还在阻塞状态进不来,只有下次轮到 t1 自己再次获得时间片时才 能开门进入
(4) 当 t1 执行完 synchronized{} 块内的代码,这时候才会从 obj 房间出来并解开门上的锁,唤醒 t2 线程并把钥匙给他。t2 线程这时才可以进入 obj 房间,锁住了门拿上钥匙,执行它的 count-- 代码;
3. 如果同步块内的线程抛出异常会发生什么?
public class ExceptionDemo { private static int count = 1; // 创建锁对象 private static Object object = new Object(); public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(()->{ synchronized (object){ System.out.println("线程t1正在执行"); // 死循环 while (count==1){ } } },"t1"); Thread t2 = new Thread(()->{ synchronized (object) { System.out.println("线程t2正在执行"); count--; } },"t2"); t1.start(); t2.start(); t1.join(); t2.join(); }}
执行结果:
可以看出线程t1执行的是死循环,所以每次线程上下文切换,线程t2都被阻塞了,拿不到锁,从而无法执行。
假如我们在线程执行过程中制造一个异常:
public class ExceptionDemo { private static int count = 1; // 创建锁对象 private static Object object = new Object(); public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(()->{ synchronized (object){ System.out.println("线程t1正在执行"); while (count==1){ // 死循环中制造异常 Integer.parseInt("a"); } } },"t1"); Thread t2 = new Thread(()->{ synchronized (object) { System.out.println("线程t2正在执行"); count--; } },"t2"); t1.start(); t2.start(); t1.join(); t2.join(); }}
执行结果:
当持有锁对象的线程在执行同步代码快中的代码时,如果出现异常,会释放锁,从而线程t2就可以拿到锁对象去执行自己同步代码块中的代码了。
总结
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注盛行IT的更多内容!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。