需求:请用线程1、2分别打印1-26,A-Z,要求打印效果是1A2B3C…26Z
需求分析:需要完成线程间交替执行,必须实现线程间通讯+锁才能完成
需求实现:下面我分别使用synchronized 和 reentrantLock 两种锁机制来分别实现,见如下代码
一、使用synchronized实现
public class SynchronizedTest {
/**
* 作为锁对象
*/
private static Object object = new Object();
/**
* 控制t1优先t2获取object锁
*/
private static boolean flag;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 1; i <= 26; i++) {
synchronized (object) {
flag = true;
System.out.print(i);
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
object.notify();
}
}
System.out.print("\n<thread 1 finish>");
});
Thread t2 = new Thread(() -> {
while (!flag) {
System.out.println("wait thread 1");
}
for (int i = 'A'; i <= 'Z'; i++) {
synchronized (object) {
object.notify();
System.out.print((char) i);
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.print("\n<thread 2 finish>");
});
t1.setName("<#1>");
t2.setName("<#2>");
t1.start();
t2.start();
t2.join();
t1.join();
System.out.println("\nall thread finsh");
}
}
执行结果:
1A2B3C4D5E6F7G8H9I10J11K12L13M14N15O16P17Q18R19S20T21U22V23W24X25Y26Z
<thread 1 finish>
<thread 2 finish>
all thread finsh
二、使用reentrantLock实现
public class LockTest {
private static ReentrantLock reentrantLock = new ReentrantLock();
private static Condition condition = reentrantLock.newCondition();
private static volatile boolean flag;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
reentrantLock.lock();
flag = true;
for (int i = 1; i <= 26; i++) {
System.out.print(i);
condition.await();
condition.signal();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
System.out.print("\n<thread 1 finish>");
}
});
Thread t2 = new Thread(() -> {
try {
while (!flag) {
System.out.println("wait thread 1");
}
reentrantLock.lock();
for (int i = 'A'; i <= 'Z'; i++) {
condition.signal();
System.out.print((char) i);
condition.await();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
System.out.print("\n<thread 2 finish>");
}
});
t1.setName("#1");
t2.setName("#2");
//用flag变量做了限制,必须等t1线程先拿到lock锁
t2.start();
t1.start();
t1.join();
t2.join();
System.out.println("\nall thread finish");
}
}
执行结果:
wait thread 1
1A2B3C4D5E6F7G8H9I10J11K12L13M14N15O16P17Q18R19S20T21U22V23W24X25Y26Z
<thread 1 finish>
<thread 2 finish>
all thread finish
以上两个执行结果中第一行的差异是因为 t1、t2在启动执行顺序不一样,这t1.start();t2.start();这两行代码执行顺序也不固定的(可能重排序),退一万步讲,及时执行顺序固定,t1,t2线程执行顺序也不确定的(取决于系统cpu时间片的分配)
总结:
线程间通讯的两大方法:
1、通过主内存通讯
如volatile关键字,带有volatile关键字的变量只要发生写操作,其他读这个变量的线程均能立刻感知
volatile的特性是:1、线程修改变量后必须立刻刷新到主内存 2、禁止重排序(线程间遵守happen before原则)
2、通过wait()/notify()方法相互通讯