多线程总是异步执行,但是在某些情况下,一个线程需要等待另一个线程执行完成后才能执行,这就是线程的同步。在Java中有多种API可以实现线程的同步,接下来就介绍几种线程同步方式。
Synchronized
public class Synchronization {
/**
* 三个线程按顺序打印10次abc
* flag为1,print1线程打印a,flag为2,print2线程打印b,flag为3,print3线程打印c
*/
public static void main(String[] args) {
Print print1 = new Print("a", 1, 2);
Print print2 = new Print("b", 2, 3);
Print print3 = new Print("c", 3, 1);
print1.start();
print2.start();
print3.start();
}
}
class Print extends Thread{
// flag和lock作为静态变量,表示所有线程共享的属性
private static int flag = 1;
private static Object lock = new Object();
private String content;
private int currFlag;
private int nextFlag;
public Print(String content,int currFlag, int nextFlag){
this.content = content;
this.currFlag = currFlag;
this.nextFlag = nextFlag;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
synchronized (lock){
while(flag != currFlag){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print(content);
flag = nextFlag;
lock.notifyAll();
}
}
}
}
Synchronized配合wait方法使线程等待,notifyAll唤醒所有线程,注意如果flag!=currFlag,线程等待,当线程被唤醒并获取到锁,将继续往下执行,所以需要使用while循环判断,如果使用if,被唤醒并获取到锁时,将会直接执行打印语句。
Reentrantlock
public class Synchronization {
/**
* 三个线程按顺序打印10次abc
* flag为1,print1线程打印a,flag为2,print2线程打印b,flag为3,print3线程打印c
*/
public static void main(String[] args) {
Print print1 = new Print("a", 1, 2);
Print print2 = new Print("b", 2, 3);
Print print3 = new Print("c", 3, 1);
print1.start();
print2.start();
print3.start();
}
}
class Print extends Thread{
// flag和lock作为静态变量,表示所有线程共享的属性
private static int flag = 1;
private static ReentrantLock lock = new ReentrantLock();
private static Condition condition = lock.newCondition();
private String content;
private int currFlag;
private int nextFlag;
public Print(String content,int currFlag, int nextFlag){
this.content = content;
this.currFlag = currFlag;
this.nextFlag = nextFlag;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
lock.lock();
try {
while(currFlag != flag){
condition.await();
}
System.out.print(content);
flag = nextFlag;
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
Reentrantlock配合await方法使线程等待,signalAll唤醒所有线程,同理如果flag!=currFlag,线程等待,当线程被唤醒并获取到锁,将继续往下执行,所以需要使用while循环判断,如果使用if,被唤醒并获取到锁时,将会直接执行打印语句。注意一个Reentrantlock对象可以创建多个Condition,这个Condition相当于阻塞队列。使用多个Condition同样也可以完成线程的同步,这里就不再展开阐述。
park与unpark
public class Synchronization {
/**
* 三个线程按顺序打印10次abc
*/
public static void main(String[] args) {
Print print1 = new Print("a");
Print print2 = new Print("b");
Print print3 = new Print("c");
print1.setNextThread(print2);
print2.setNextThread(print3);
print3.setNextThread(print1);
print1.start();
print2.start();
print3.start();
LockSupport.unpark(print1);
}
}
class Print extends Thread{
private String content;
private Thread nextThread;
public Print(String content){
this.content = content;
}
public void setNextThread(Thread nextThread) {
this.nextThread = nextThread;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
LockSupport.park();
System.out.print(content);
LockSupport.unpark(nextThread);
}
}
}
park和unpark的思路就是上来开启所有线程时,先将所有的线程进入等待状态,之后打开输出a的线程print1,print1输出a,之后唤醒打印b的print2线程,此时print1并没有进入等待,而是进入下一轮循环,调用了park方法而等待。