/**
* 每天一个知识点day76 TODO Fragment重叠问题 、CountDownLatch和CyclicBarrier的理解和区别
* <p>
* Fragment的应用场景有很多,比如我们底部一个导航栏,
* 点击导航项显示不同的fragment,或者和ViewPager配合使用等。
* 但是当我们横竖屏切换时,或者锁屏过段时间再进入app时,很可能会遇见重叠的问题。
* <p>
* 原因:
* 当我们旋转屏幕的时候,activity被销毁重新创建,并且在销毁之前执行了
* onSaveInstanceState方法,这个方法会保存activity的一些信息,其中就包括
* 添加过的fragment,当activity被重新创建时,会初始化其中的变量,这个时候
* 点击底部导航的话会重新去添加fragment,也就导致了重叠问题。
* 还有一种可能也会造成fragment重叠的问题,就是当内存不足时activity被系统回收时,
* 再次进入也会造成重叠的问题,原因也是因为onSaveInstanceState()
* 方法保存了activity的一些数据.
* <p>
* 解决办法:
* 1.不保存activity信息(不推荐)
*
* @Override protected void onSaveInstanceState(Bundle outState) {
* //super.onSaveInstanceState(outState);
* }
* 2.旋转屏幕时不让activity走生命周期方法(推荐)
* android:configChanges=“keyboardHidden|orientation|screenSize”>即可。
* 3.在onSaveInstanceState()中去保存fragment,当activity被恢复时,取出这些fragment。
* 在onCreate方法中判断savedInstanceState!=null就获取保存的fragment
* if (savedInstanceState != null) {
* //获取保存的fragment 没有的话返回null
* homeFragment = (HomeFragment) getSupportFragmentManager().
* getFragment(savedInstanceState, HOME_FRAGMENT_KEY);
* <p>
* }else{
* initFragment();
* }
* @Override protected void onSaveInstanceState(Bundle outState) {
* if(homeFragment !=null){
* getSupportFragmentManager().putFragment(outState, HOME_FRAGMENT_KEY, homeFragment);
* }
* super.onSaveInstanceState(outState);
* }
* <p>
* TODO CountDownLatch和CyclicBarrier的理解和区别
*
* CountDownLatch和CyclicBarrier都是java.util.concurrent包下面的多线程工具类。
* 从字面上理解,CountDown表示减法计数,Latch表示门闩的意思,计数为0的时候就可以打开门闩了。
* Cyclic Barrier表示循环的障碍物。
* 两个类都含有这一个意思:对应的线程都完成工作之后再进行下一步动作,也就是大家都准备好之后再进行下一步。
* 然而两者最大的区别是,进行下一步动作的动作实施者是不一样的。
* 这里的“动作实施者”有两种,一种是主线程(即执行main函数),
* 另一种是执行任务的其他线程,后面叫这种线程为“其他线程”,区分于主线程。
* 对于CountDownLatch,当计数为0的时候,下一步的动作实施者是main函数;
* 对于CyclicBarrier,下一步动作实施者是“其他线程”
*
* CountDownLatch:
*
* CountDownLatch使一个线程等待其他线程各自执行完毕后再执行。
* CountDownLatch是通过一个计数器来实现的,计数器的初始值是线程的数量。
* 每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,
* 然后在闭锁上等待的线程就可以恢复工作了。
* CountDownLatch构造器中传入计数器数量。
* 类中有三个方法是最重要的:
* <p>
* //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
* public void await() throws InterruptedException { };
* <p>
* //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
* public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };
* <p>
* //将count值减1
* public void countDown() { };
*
* CyclicBarrier:
* 它的作用就是会让所有线程都等待完成后才会继续下一步行动。
* 构造方法:
* public CyclicBarrier(int parties)
* public CyclicBarrier(int parties, Runnable barrierAction)
* parties 是参与线程的个数
* 第二个构造方法有一个 Runnable 参数,这个参数的意思是最后一个到达线程要做的任务
*
* 线程调用 await() 表示自己已经到达栅栏。
* 可完成需求:一个线程组的线程需要等待所有线程完成任务后再继续执行下一次任务。
*
*
* CountDownLatch和CyclicBarrier区别?
* 都有让多个线程等待同步然后再开始下一步动作的意思,
* 但是CountDownLatch的下一步的动作实施者是主线程,具有不可重复性,是一次性的。
* 而CyclicBarrier的下一步动作实施者还是“其他线程”本身,是可循环利用的。
* CountDownLatch更多依赖经典的AQS机制和CAS机制来控制器内部状态的更迭和计数器本身的变化,
* 而CyclicBarrier更多依靠可重入Lock等机制来控制其内部并发安全性和一致性。
*
*/
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
// countTest();
cyclicTest();
}
private void cyclicTest() {
CyclicBarrier barrier = new CyclicBarrier(3);
for (int i = 0; i < barrier.getParties(); i++) {
new Thread(new MyRunnable(barrier), "队友" + i).start();
}
System.out.println("main function is finished.");
}
private static class MyRunnable implements Runnable {
private CyclicBarrier barrier;
public MyRunnable(CyclicBarrier barrier) {
this.barrier = barrier;
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
try {
Random rand = new Random();
int randomNum = rand.nextInt((3000 - 1000) + 1) + 1000;//产生1000到3000之间的随机整数
Thread.sleep(randomNum);
System.out.println(Thread.currentThread().getName() + ", 通过了第" + i + "个障碍物, 使用了 " + ((double) randomNum / 1000) + "s");
this.barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
private void countTest() {
final CountDownLatch latch = new CountDownLatch(2);
System.out.println("主线程开始执行…… ……");
//第一个子线程执行
ExecutorService es1 = Executors.newSingleThreadExecutor();
es1.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
System.out.println("子线程:" + Thread.currentThread().getName() + "执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
}
});
es1.shutdown();
//第二个子线程执行
ExecutorService es2 = Executors.newSingleThreadExecutor();
es2.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("子线程:" + Thread.currentThread().getName() + "执行");
latch.countDown();
}
});
es2.shutdown();
//第二个子线程执行
ExecutorService es3 = Executors.newSingleThreadExecutor();
es3.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("子线程:" + Thread.currentThread().getName() + "执行");
latch.countDown();
}
});
es3.shutdown();
System.out.println("等待两个线程执行完毕…… ……");
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("两个子线程都执行完毕,继续执行主线程");
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
网友评论