线程同步的方法有哪几种_异步和同步的区别在哪

线程同步的方法有哪几种_异步和同步的区别在哪二、通过Condition的awiat和signal前言:在多线程中线程的执行顺序是依靠哪个线程先获得到CUP的执行权谁就先执行,虽然说可以通过线程的优先权进行设置,但是他只是CUP执行权的概率高点,但是也不一定必须先执行。在这种情况下如何保证线程按

二、通过Condition的awiat和signal   前言:   在多线程中线程的执行顺序是依靠哪个线程先获得到CUP的执行权谁就先执行,虽然说可以通过线程的优先权进行设置,但是他只是CUP执行权的概率高点,但是也不一定必须先执行。在这种情况下如何保证线程按照一定的顺序进行执行,今天就来一个大总结,分别介绍一下几种方式。   一、通过Object的wait和notify   二、通过Condition的awiat和signal   三、通过一个阻塞队列   四、通过两个阻塞队列   五、通过SynchronousQueue    六、通过线程池的Callback回调   七、通过同步辅助类CountDownLatch   八、通过同步辅助类CyclicBarrier   一、通过Object的wait和notify   写一个测试了Test,加上main方法,在写一个内部类Man进行测试。main方法如下,他进行创建两个线程,传进去Runnable对象。   public static boolean flag = false; public static int num = 0; public static void main(String[] args) {     Man man = new Man();     new Thread(() -> {         man.getRunnable1();     }).start();     new Thread(() -> {         man.getRunnable2();     }).start(); }   getRunnable1和getRunnable2分别表示两个需要执行的任务,在两个线程中进行,方法1用于数据的生产,方法二用于数据的,数据的初始值为num = 0,为了保证生产和平衡需要使用wait和notify方法,这两个方法的使用必须是要加锁的,因此使用synchronized进行加锁使用,为了演示这个效果,我们加上一个sleep方法模拟处理时间,如下:   public static class Man {          public synchronized void getRunnable1() {         for (int i = 0; i < 20; i++) {             while (flag) {                 try {                     wait();                 } catch (InterruptedException e) {                     e.printStackTrace();                 }             }             System.out.println(“生产出:” + (++num) + “个”);             flag = true;             notify();         }     }          public synchronized void getRunnable2() {         for (int i = 0; i < 20; i++) {             while (!flag) {                 try {                     wait();                 } catch (InterruptedException e) {                     e.printStackTrace();                 }             }             //模拟加载时间             try {                 Thread.sleep(1000);             } catch (InterruptedException e) {                 e.printStackTrace();             }             System.out.println(“取出出:” + (num–) + “个”);             System.out.println(“——————“);             flag = false;             notify();         }     } }   分析它的加载流程,从方法1进行分析,由于flag的初始条件为false,所以方法1不进入等待,直接进行生产,生产完成成之后,更新flag的值为true,同时notify下一个方法2的wait方法,使其变为唤醒状态。这时候由于方法1加锁了,无法执行方法1其他部分,当方法1执行完毕,方法1才有可能执行,但是方法1的flag已经为true,进入到wait里面又处于阻塞状态,所以这时候只能执行方法2了。由于方法2被唤醒了,阻塞解除,接下来就数据,当完毕又再次让flag变为false,notify方法1解除阻塞,再次执行方法1,就这样不断的循环,保证了不同线程的有序执行,直到程序终止。   运行效果如下:   
线程同步的方法有哪几种_异步和同步的区别在哪   二、通过Condition的awiat和signal   上面第一个的实现是一个阻塞,一个等待的方式保证线程有序的执行,但是不能进行两个线程之间进行通信,而接下来介绍的Condition就具备这样的功能。要Condition对象首先先得Lock对象,他是在jdk1.5之后增加的,比synchronized性能更好的一种锁机制。和上面的类似,拷贝一份代码,看看main方法:   public static boolean flag = false; public static int num = 0; public static void main(String[] args) {     Man man = new Man();     new Thread(() -> {         man.getRunnable1();     }).start();     new Thread(() -> {         man.getRunnable2();     }).start(); }   情况和第一个实现方法分析一致,这里不重复了。主要看内部类Man中的方法1和方法2。先手创建锁对象,把synchronized改为使用Lock加锁,其次通过Lock创建Condition对象,替换掉Object类的wait方法为Condition的await方法,最后换掉notify方法为signal方法即可,执行原理和上面分析一致,代码如下:   public static class Man {     public static ReentrantLock lock = new ReentrantLock();     public static Condition condition = lock.newCondition();     public void getRunnable1() {         lock.lock();         try {             for (int i = 0; i < 20; i++) {                 while (flag) {                     try {                         condition.await();                     } catch (InterruptedException e) {                         e.printStackTrace();                     }                 }                 System.out.println(“生产出:” + (++num) + “个”);                 flag = true;                 condition.signal();             }         } finally {             lock.lock();         }     }     public void getRunnable2() {         lock.lock();         try {             for (int i = 0; i < 20; i++) {                 while (!flag) {                     try {                         condition.await();                     } catch (InterruptedException e) {                         e.printStackTrace();                     }                 }                 try {                     Thread.sleep(1000);                 } catch (InterruptedException e) {                     e.printStackTrace();                 }                 System.out.println(“取出出:” + (num–) + “个”);                 System.out.println(“——————“);                 flag = false;                 condition.signal();             }         } finally {             lock.unlock();         }     } }   执行结果如下:   
线程同步的方法有哪几种_异步和同步的区别在哪   这是我的iOS开发交流群:不管你是小白还是大牛欢迎入驻,可以一起分享经验,讨论技术,共同学习成长!   另附上一份各好友收集的大厂面试题,需要iOS开发学习资料、面试真题,进群即可自行下载!   
线程同步的方法有哪几种_异步和同步的区别在哪此处,立即与iOS大牛交流学习   三、通过一个阻塞队列   上面的两个方法实现起来代码比较繁琐,如果通过阻塞队列来实现会更加简洁,这里采用常用的容量为64的ArrayBlockingQueue来实现。main方法如下:   public static void main(String[] args) {     Man man = new Man();     new Thread(() -> {         man.getRunnable1();     }).start();     new Thread(() -> {         man.getRunnable2();     }).start(); }   主要来看Man中的方法1和方法2,方法1中生产数据,这里把生产的数据存进队列里面,同时方法2进行取数据,如果方法1放满了或者方法2取完了就会被阻塞住,等待方法1生产好了或者方法2取出了,然后再进行。代码如下:   public static class Man {     ArrayBlockingQueue queue = new ArrayBlockingQueue<Integer>(64);     public void getRunnable1() {         for (int i = 0; i < 8; i++) {             System.out.println(“生产出:” + i + “个”);             try {                 queue.put(i);             } catch (InterruptedException e) {                 e.printStackTrace();             }         }         System.out.println(“—————生产完毕—————–“);     }     public void getRunnable2() {         for (int i = 0; i < 8; i++) {             try {                 int num = (int) queue.take();                 System.out.println(“取出出:” + num);             } catch (InterruptedException e) {                 e.printStackTrace();             }         }     } }   很明显使用阻塞队列代码精炼了很多,在这还可以发现这个阻塞队列是具有缓存功能的,想很多Android中网络访问框架内部就是使用这个进行缓存的,例如Volley、Okhttp等等。   运行效果如下:   
线程同步的方法有哪几种_异步和同步的区别在哪   四、通过两个阻塞队列   使用一个阻塞队列能够实现线程同步的功能,两个阻塞队列也可以实现线程同步。原理是ArrayBlockingQueue他是具有容量的,如果把他的容量定位1则意味着他只能放进去一个素,第二个方进行就会就会被阻塞。按照这个原理进行来实现,定义两个容量为1的阻塞队列ArrayBlockingQueue,一个存放数据,另一个用于控制次序。main方法和上面一致,主要来看看Man类中的两个方法:   static class Man {     //数据的存放     ArrayBlockingQueue queue1 = new ArrayBlockingQueue<Integer>(1);     //用于控制程序的执行     ArrayBlockingQueue queue2 = new ArrayBlockingQueue<Integer>(1);     {         try {             //queue2放进去一个素,getRunnable2阻塞             queue2.put(22222);         } catch (InterruptedException e) {             e.printStackTrace();         }     }     public void getRunnable1() {         new Thread(() -> {             for (int j = 0; j < 20; j++) {                 try {                     //queue1放进一个素,getRunnable1阻塞                     queue1.put(j);                     System.out.println(“存放   线程名称:” + Thread.currentThread().getName() + “-数据为-” + j);                 } catch (InterruptedException e) {                     e.printStackTrace();                 }                 try {                     Thread.sleep(500);                 } catch (InterruptedException e) {                     e.printStackTrace();                 }                 try {                     //queue2取出素,getRunnable2进入                     queue2.take();                 } catch (InterruptedException e) {                     e.printStackTrace();                 }             }         }).start();     }     public void getRunnable2() {         new Thread(() -> {             for (int j = 0; j < 20; j++) {                 try {                     //queue2放进一个素,getRunnable2阻塞                     queue2.put(22222);                 } catch (InterruptedException e) {                     e.printStackTrace();                 }                 try {                     //queue1放进一个素,getRunnable1进入                     int i = (int) queue1.take();                     System.out.println(”   线程名称:” + Thread.currentThread().getName() + “-数据为-” + i);                 } catch (InterruptedException e) {                     e.printStackTrace();                 }             }         }).start();     } }   再次提醒queue2用于控制程序的执行次序,并无实际含义。最后看看运行效果,存一个、取一个很清晰,如下:   
线程同步的方法有哪几种_异步和同步的区别在哪   五、通过SynchronousQueue   SynchronousQueue不同于一般的数据等线程,而是线程等待数据,他是一个没有数据缓冲的BlockingQueue,生产者线程对其的插入操作put必须等待消费者的移除操作take,反过来也一样。通过这一特性来实现一个多线程同步问题的解决方案,代码如下:   /  * 使用阻塞队列SynchronousQueue  * offer将数据插入队尾  * take取出数据,如果没有则阻塞,直到有数据在到  */ public static void test() {     SynchronousQueue queue = new SynchronousQueue();     ExecutorService executorService = Executors.newSingleThreadExecutor();     executorService.execute(new Runnable() {         @Override         public void run() {             try {                 Thread.sleep(5000);                 queue.offer(9);             } catch (InterruptedException e) {                 e.printStackTrace();             }         }     });     try {         int take = (int) queue.take();         System.out.println(take);     } catch (InterruptedException e) {         e.printStackTrace();     } }   子线程中进行设置数据,而主线程数据,如果子线程没执行完毕,子线程没有执行完毕主线程就会被阻塞住不能执行下一步。   六、通过线程池的Callback回调   在线程的创建中,有一种创建方法可以返回线程结果,就是callback,他能返回线程的执行结果,通过子线程返回的结果进而在主线程中进行操作,也是一种同步方法,这种同步在Android中特别适用,例如Android中的AsyncTask源码中任务的创建部分。代码如下:   private static void test() {     ExecutorService executorService = Executors.newFixedThreadPool(5);     Future<Boolean> submit = executorService.submit(new Callable<Boolean>() {         @Override         public Boolean call() throws Exception {             return false;         }     });     try {         if (submit.get()) {             System.out.println(true);         } else {             System.out.println(false);         }     } catch (InterruptedException e) {         e.printStackTrace();     } catch (ExecutionException e) {         e.printStackTrace();     } }   七、通过同步辅助类CountDownLatch   CountDownLatch是一个同步的辅助类,允许一个或多个线程,等待其他一组线程完成操作,再继续执行。他类实际上是使用计数器的方式去控制的,在创建的时候传入一个int数值每当我们调用countDownt()方法的时候就使得这个变量的值减1,而对于await()方法则去判断这个int的变量的值是否为0,是则表示所有的操作都已经完成,否则继续等待。可以理解成倒计时锁。   public class Test7 {     public static void main(String[] args) {         //启动两个线程,分别执行完毕之后再执行主线程         CountDownLatch countDownLatch = new CountDownLatch(2);           //线程1执行         Thread thread1 = new Thread(() -> {             try {                 Thread.sleep(2000);             } catch (InterruptedException e) {                 e.printStackTrace();             }             System.out.println(Thread.currentThread().getName() + “线程执行完毕”);             countDownLatch.countDown();         });         //线程2执行         Thread thread2 = new Thread(() -> {             System.out.println(Thread.currentThread().getName() + “线程执行完毕”);             countDownLatch.countDown();         });             thread1.start();         thread2.start();         try {             countDownLatch.await();         } catch (InterruptedException e) {             e.printStackTrace();         }           //执行主线程         System.out.println(“主线程执行完毕”);     } }   结果如下:   
线程同步的方法有哪几种_异步和同步的区别在哪   八、通过同步辅助类CyclicBarrier   CyclicBarrier是一个同步的辅助类,和上面的CountDownLatch比较类似,不同的是他允许一组线程相互之间等待,达到一个共同点,再继续执行。可看成是个障碍,所有的线程必须到齐后才能一起通过这个障碍。   public class Test8 {     public static void main(String[] args) {         //启动两个线程,分别执行完毕之后再执行主线程         CyclicBarrier barrier  = new CyclicBarrier(2, () -> {             //执行主线程             System.out.println(“主线程执行完毕”);           });           //线程1执行         Thread thread1 = new Thread(() -> {             try {                 Thread.sleep(2000);             } catch (InterruptedException e) {                 e.printStackTrace();             }               System.out.println(Thread.currentThread().getName() + “线程执行完毕”);               try {                 barrier.await();             } catch (InterruptedException e) {                 e.printStackTrace();             } catch (BrokenBarrierException e) {                 e.printStackTrace();             }         });           //线程2执行         Thread thread2 = new Thread(() -> {             System.out.println(Thread.currentThread().getName() + “线程执行完毕”);             try {                 barrier.await();             } catch (InterruptedException e) {                 e.printStackTrace();             } catch (BrokenBarrierException e) {                 e.printStackTrace();             }         });             thread1.start();         thread2.start();     } }   运行结果:   
线程同步的方法有哪几种_异步和同步的区别在哪   查看更多: iOS面试题合集

2024最新激活全家桶教程,稳定运行到2099年,请移步至置顶文章:https://sigusoft.com/99576.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。 文章由激活谷谷主-小谷整理,转载请注明出处:https://sigusoft.com/21149.html

(0)
上一篇 2024年 9月 15日
下一篇 2024年 9月 15日

相关推荐

关注微信