第一类: join、wait¬ify
第二类:Synchronized、ReentrantLock、ReentrantReadWriteLock、Condition 、StampedLock
第三类:Semaphore、CountDownLatch、CyclicBarrier
上面是涉及到线程间通讯的一些方法或者类,这里做一下整理,说到线程间同步,就少不了一个关键字 volatile,它的作用是保证变量在各个线程之间保持同步,如果你读过Android系统关于线程池的源码,你就会发现在系统定义好的几个线程池类中都会使用volatile关键字来修饰全局变量。
第一类是靠线程自身提供的一些方法来保证线程间的同步行为,比较简单。
join:join是保持线程同步的一种方式,你可以把它简单的理解为插队。比如说现在我们有两个任务A和B需要执行,但是在执行任务A的过程中需要等待任务B执行完成之后才能继续执行。这个时候使用join关键字就比较合适,就好比说任务B插队到任务A的前面,只有等任务B完成了之后任务A才能继续执行。
不使用关键字join和使用join关键字的程序运行图比较
图1:无join,异步
图2: 有join,同步
新建两个线程ThreadA和ThreadB,运行代码测试看结果
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//程序入口(我这里是一个Android程序中的Fragment的入口)
//如果是Java程序,只需将这个两句放到main方法中即可
mThreadA = new ThreadA();
mThreadA.start();
}
private class ThreadA extends Thread {
@Override
public void run() {
super.run();
Log.e(TAG, "A start");
try {
ThreadB mThreadB = new ThreadB();
mThreadB.start();
// mThreadB.join();//打开注释就是使用join关键字
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "A " + e.toString());
}
Log.e(TAG, "A end");
}
}
private class ThreadB extends Thread {
@Override
public void run() {
super.run();
Log.e(TAG, "B start");
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "B " + e.toString());
}
Log.e(TAG, "B end");
}
}
上面的代码运行之后,控制台输出
03-02 15:35:57.054 5345-5392/? E/ThreadRun: A start
03-02 15:35:57.055 5345-5392/? E/ThreadRun: A end
03-02 15:35:57.055 5345-5393/? E/ThreadRun: B start
03-02 15:35:58.056 5345-5393/? E/ThreadRun: B end
与图1相符。
找到代码中 // mThreadB.join();
取消注释,再次运行,控制台输出
03-02 16:34:16.131 6793-6825/? E/ThreadRun: A start
03-02 16:34:16.132 6793-6828/? E/ThreadRun: B start
03-02 16:34:17.132 6793-6828/? E/ThreadRun: B end
03-02 16:34:17.133 6793-6825/? E/ThreadRun: A end
与图2相符。
这就是一个简单的使用join关键字保持线程同步的例子。