线程间通信,就要联想到Object的wait、notify、notifyAll和Lock.new Condition()的await、signal、signalAll。

【1】需求一,三个线程顺序打印ABC,每个线程打印十次,ABC分别为线程名字

效果如下:

A
A
A
A
A
A
A
A
A
A
//BC依次排列

使用synchronized、wait、notifyAll实例代码如下:

public class TestSwapThread {
public static void main(String[] args){
//创建三把锁
final Object obj1 = new Object();
final Object obj2 = new Object();
final Object obj3 = new Object();
SwapThread swapThread = new SwapThread(obj1,obj2);
SwapThread swapThread2 = new SwapThread(obj3,obj1);
SwapThread swapThread3 = new SwapThread(obj2,obj3);
swapThread.setName("A");
swapThread.start();
//这里来保证次序
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
swapThread2.setName("B");
swapThread2.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
swapThread3.setName("C");
swapThread3.start();
}
}

class SwapThread extends Thread{

private Object obj1 = null;
private Object obj2 = null;

public SwapThread(Object obj1,Object obj2){
this.obj1 = obj1;
this.obj2 = obj2;
}

@Override
public void run() {
synchronized (obj1){
System.out.println(Thread.currentThread().getName()+"准备进入第二层");
synchronized (obj2){
for (int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+" : "+i);
}
//nofityAll并不会释放锁,但是同步代码块执行完会自动释放锁
obj2.notifyAll();
}//这里释放内存锁
//nofityAll并不会释放锁,但是同步代码块执行完会自动释放锁
obj1.notifyAll();
System.out.println(Thread.currentThread().getName()+" : end");
}//这里释放外层锁
}
}

使用lock实例如下

public class TestSwapThread3 {
public static void main(String[] args){
//这里也可作为swapThread3 构造参数传入
//Lock lock = new ReentrantLock();
SwapThread3 swapThread3 = new SwapThread3();
new Thread(swapThread3,"A").start();
new Thread(swapThread3,"B").start();
new Thread(swapThread3,"C").start();

}
}

class SwapThread3 implements Runnable{
//共享数据--同一把锁 ReentrantLock
private Lock lock =new ReentrantLock() ;

@Override
public void run() {
//加锁
lock.lock();
try {
int count =0;
while (count<10){
System.out.println(Thread.currentThread().getName()+" : "+count );
count++;

}
}finally {
//释放锁
lock.unlock();
}

}
}

程序执行结果如下:

线程间通信实例之轮流打印ABC_ide

其实lock加锁/释放锁和顺序打印本质是保存线程执行的次序,严格意义从理论来讲,第二种方式在某些情况下是不太正确的。


【2】需求二,三个线程轮流打印ABC,每个线程打印十次,ABC分别为线程名字

效果如下:

A
B
C
//....

这时,就对线程通信要求高了。


使用synchronized、wait、notifyAll实例代码如下:

public class TestSwapThread4 {
public static void main(String[] args){

final Object obj1 = new Object();
final Object obj2 = new Object();
final Object obj3 = new Object();
SwapThread4 swapThread = new SwapThread4(obj1,obj2);
SwapThread4 swapThread2 = new SwapThread4(obj2,obj3);
SwapThread4 swapThread3 = new SwapThread4(obj3,obj1);
swapThread.setName("A");
swapThread.start();

swapThread2.setName("B");
swapThread2.start();

swapThread3.setName("C");
swapThread3.start();

}
}

class SwapThread4 extends Thread{

private Object obj1 = null;
private Object obj2 = null;

public SwapThread4(Object obj1,Object obj2){
this.obj1 = obj1;
this.obj2 = obj2;
}

@Override
public void run() {
for (int i=0;i<10;i++){
synchronized (obj1){
synchronized (obj2){
System.out.println(Thread.currentThread().getName()+" : "+i);
//notifyAll并不会释放锁,代码块结束后释放锁
obj2.notifyAll();
}
try {
//避免程序不能正常结束
if (i==9){
obj1.notifyAll();
}else{
//wait方法将会释放锁标志,进入阻塞状态,等到唤醒
obj1.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}

}
}
}

测试结果如下:

线程间通信实例之轮流打印ABC_ide_02


使用Lock+Condition实例如下:

public class TestSwapThread2 {

public static void main(String[] args){
Lock lock = new ReentrantLock();
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
Condition conditionC = lock.newCondition();
SwapThread2 swapThread = new SwapThread2(lock,conditionA,conditionB);
SwapThread2 swapThread2 = new SwapThread2(lock,conditionB,conditionC);
SwapThread2 swapThread3 = new SwapThread2(lock,conditionC,conditionA);

swapThread.setName("A");
swapThread.start();

swapThread2.setName("B");
swapThread2.start();

swapThread3.setName("C");
swapThread3.start();

}
}

class SwapThread2 extends Thread{

private Lock lock ;
private Condition condition1;
private Condition condition2 ;

public SwapThread2(Lock lock,Condition condition1,Condition condition2){
this.lock = lock;
this.condition1=condition1;
this.condition2=condition2;
}


@Override
public void run() {
//加锁
lock.lock();
try {
int count =0;
while (count<10){
//唤醒等待condition2的线程
condition2.signal();
System.out.println(Thread.currentThread().getName()+" : "+count );
count++;
if (count==10){
//避免程序不能结束
condition1.signalAll();
}else{
try {
//当前线程进入阻塞,等到condition1被唤醒
condition1.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}finally {
lock.unlock();
}
}
}

【3】需求三-如果每次ABC打印次数不一样且要交替进行呢?

实例代码如下:

public class TestABCAlternate {

public static void main(String[] args) {
AlternateDemo ad = new AlternateDemo();
//创建线程A,循环20遍loopA
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 20; i++) {
ad.loopA(i);
}
}
}, "A").start();
//创建线程B,循环20遍loopB
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 20; i++) {
ad.loopB(i);
}
}
}, "B").start();
//创建线程C,循环20遍loopC
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 20; i++) {
ad.loopC(i);
System.out.println("-----------------------------------");
}
}
}, "C").start();
}
}

class AlternateDemo{
//当前正在执行线程的标记
private int number = 1;
//创建ReentrantLock实例
private Lock lock = new ReentrantLock();
//分别创建三个lock的Condition 实例
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();

/**
* @param totalLoop : 循环第几轮
*/
public void loopA(int totalLoop){
lock.lock();
try {
//1. 判断
if(number != 1){
//是当前线程阻塞,等到condition1被唤醒
condition1.await();
}
//2. 打印
for (int i = 1; i <= 1; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
}
//3. 唤醒
number = 2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}

public void loopB(int totalLoop){
lock.lock();
try {
//1. 判断
if(number != 2){
condition2.await();
}
//2. 打印
for (int i = 1; i <= 1; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
}
//3. 唤醒
number = 3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}

public void loopC(int totalLoop){
lock.lock();
try {
//1. 判断
if(number != 3){
condition3.await();
}
//2. 打印一遍
for (int i = 1; i <= 1; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
}
//3. 唤醒第一个线程 loopA
number = 1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}

}

Lock与Condition详解讲解参考博文:Lock与Condition使用讲解。