前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java 总结几种线程异步转同步的方法

java 总结几种线程异步转同步的方法

作者头像
allsmallpig
发布2021-02-25 11:22:33
7160
发布2021-02-25 11:22:33
举报
文章被收录于专栏:allsmallpi博客

转载自https://blog.csdn.net/Veson__/article/details/53898890

在做一款app的时候,用到了一个异步执行的api,而我想要的是同步执行,查了一些资料,对几种情况下的线程同步做一下总结。

一、共享资源的同步

问题:当一个资源被多个线程访问会发生错误,只能允许一个线程访问时。

1.syschronized实现

使用syschonized关键字可对某个块或者方法进行限制访问,即当一个线程获得块或者方法的访问权后,其他线程将不能访问。

[java]  view plain copy

  1. public class synchronize_test implements Runnable{  
  2. int num1 = 10;  
  3. int num2 = 10;  
  4. int num3 = 10;  
  5. public void synchonized_test() {  
  6. synchronized(this) {  
  7. while(num1 > 0) { //只有得到锁的线程才能访问
  8.                 System.out.println(Thread.currentThread().getName() + "访问num1=" + num1--);  
  9.             }  
  10.         }  
  11. synchronized(this) { //只有得到锁的线程才能访问,即使它还没有开始访问这儿,因为同步锁的作用对象是对象中的所有同步块
  12. while(num2 > 0) {  
  13.                 System.out.println(Thread.currentThread().getName() + "访问num2=" + num2--);  
  14.             }  
  15.         }  
  16. while(num3 > 0) { //未得到锁的线程可访问此资源(非同步块)
  17.             System.out.println(Thread.currentThread().getName() + "访问num3=" + num3--);  
  18.         }  
  19.     }  
  20. @Override
  21. public void run() {  
  22.         synchonized_test();  
  23.     }  
  24. public static void main(String[] args) {  
  25.         synchronize_test sys = new synchronize_test();  
  26.         Thread t1 = new Thread(sys);  
  27.         Thread t2 = new Thread(sys);  
  28.         t1.start();  
  29.         t2.start();  
  30.     }  
  31. }  

当使用sychronized修饰某个方法(非static)时,作用对象将是这个方法所属的对象,与同步块同理。而如果synchronized修饰的是static方法或变量时,作

用对象将是static所在的类而非某个对象,因为static方法不属于任何一个对象,而是属于类。

2.Lock实现

可以看出,syschronized的作用的是对象或者类,这显然不太灵活,而Lock则比其更加灵活一些。

[java]  view plain copy

  1. public class Lock_Test implements Runnable{  
  2. int num1 = 10;  
  3. int num2 = 10;  
  4.     Lock lock1 = new ReentrantLock();  
  5.     Lock lock2 = new ReentrantLock();  
  6. public void synchonized_test() {  
  7.         lock1.lock(); //获得lock1
  8. try {  
  9. while(num1 > 0) { //只有得到锁的线程才能访问
  10.                 System.out.println(Thread.currentThread().getName() + "访问num1=" + num1--);  
  11.             }  
  12.         } finally {  
  13.             lock1.unlock();  
  14.         }  
  15.         lock2.lock();//获得lock2
  16. try {  
  17. while(num2 > 0) {  
  18.                 System.out.println(Thread.currentThread().getName() + "访问num2=" + num2--);  
  19.             }  
  20.         } finally {  
  21. //在finally中解锁以防死锁
  22.             lock2.unlock(); //解锁
  23.         }  
  24.     }  
  25. @Override
  26. public void run() {  
  27.         synchonized_test();  
  28.     }  
  29. public static void main(String[] args) {  
  30.         Lock_Test lt = new Lock_Test();  
  31.         Thread t1 = new Thread(lt);  
  32.         Thread t2 = new Thread(lt);  
  33.         t1.start();  
  34.         t2.start();  
  35.     }  
  36. }  

这里使用的是ReentrantLock,另外还有ReadWriteLock。

ReentranLock的优点(摘自:https://github.com/pzxwhc/MineKnowContainer/issues/16

lock在获取锁的过程可以被中断。

lock可以尝试获取锁,如果锁被其他线程持有,则返回 false,不会使当前线程休眠。

lock在尝试获取锁的时候,传入一个时间参数,如果在这个时间范围内,没有获得锁,那么就是终止请求。

synchronized 会自动释放锁,lock 则不会自动释放锁。

二、异步转同步

问题:某些API是异步的,而我们想让其同步。如:A、B两个方法异步执行,由于某些需求,想让A方法执行完之后再执行B方法。

1.CountDownLatch解决

使用CountDownLatch可以实现同步,它好比计数器,在实例CountDownLatch对象的时候传入数字,每使用一次 .countDown() 方法计数减1,当数字减到0时, .await()方法后的代码将可以执行,未到0之前将一直阻塞等待。

[java]  view plain copy

  1. import java.util.concurrent.CountDownLatch;  
  2. public class CountDownLatch_test implements Runnable{  
  3. private Integer num = null;  
  4. private static CountDownLatch latch;  
  5. public void setNumber() {  
  6.         num = 1;  
  7.     }  
  8. public int getNumber() {  
  9. return this.num;  
  10.     }  
  11. @Override
  12. public void run() {  
  13. if(Thread.currentThread().getName().equals("Thread-0")) { //t2线程
  14. try {  
  15.                 Thread.sleep(5000);  
  16.             } catch (InterruptedException e) {  
  17.                 e.printStackTrace();  
  18.             }  
  19. this.setNumber();  
  20.             latch.countDown(); //计数减1
  21.         }  
  22. else if(Thread.currentThread().getName().equals("Thread-1")){ //t1线程
  23. try {  
  24.                 latch.await(); //阻塞等待计数为0
  25.             } catch (InterruptedException e) {  
  26.                 e.printStackTrace();  
  27.             }  
  28.             System.out.println("num = " + this.getNumber());  
  29.         }  
  30.     }  
  31. public static void main(String[] args) {  
  32.         CountDownLatch_test c = new CountDownLatch_test();  
  33.         latch = new CountDownLatch(1);  
  34.         Thread t1 = new Thread(c);  
  35.         Thread t2 = new Thread(c);  
  36.          t1.start();  
  37.          t2.start();  
  38.     }  
  39. }  

如代码所示,t1线程获得num的值,t2线程给num赋值,显然t2需要在t1之前执行结束,而t2执行的时间却比t1长,故使用CountDown对t1进行阻塞等待t2完成。

此外,也可以给await(设置参数),到达一定时间计数未变为0也可执行。

********其他方法待学习*******

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018/05/31 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档