前言
合理的使用Java多线程可以更好地利用服务器资源。往往线程都是独立运行,互不干扰。但是当我们需要多个线程之间相互协作的时候,就需要我们掌握Java线程的通信方式。 java 线程间的通信是面试常见的问题,对于两个线程分别输出奇偶数,则是经典中经典,下面来探讨输出奇偶数的几种写法。
1,synchronized 实现
代码如下:
public class PrintOddEvenUseSync {
public static void main(String[] args) {
Num num = new Num();
Even even = new Even(num);
Odd odd = new Odd(num);
new Thread(even, "EVEN").start();
new Thread(odd, "ODD").start();
}
}
class Even implements Runnable {
private Num num;
public Even(Num num) {
this.num = num;
}
@Override
public void run() {
while (num.num < 1000) {
synchronized (num) {
if (num.isEven()) {
System.out.println(Thread.currentThread().getName() + ":" + num.num++);
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
class Odd implements Runnable {
private Num num;
public Odd(Num num) {
this.num = num;
}
@Override
public void run() {
while (num.num < 1000) {
synchronized (num) {
if (num.isOdd()) {
System.out.println(Thread.currentThread().getName() + ":" + num.num++);
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
这种方式来源于一位网友的写法:blog.csdn.net/x541211190/… 显然这种方式是不可取的,原因在于上面的方法不是线程交替打印,一个线程可以持续获取多次锁的,这里只不过用判断规避了这种情况而已。
2,使用 wait notify 经典写法
package oddeven;
/**
* 使用wait notify
* 线程间通讯
* 推荐使用
*/
public class PrintOddEvenUseWait {
public static void main(String[] args) {
Num num = new Num();
OddThread oddThread = new OddThread(num);
EvenThread evenThread = new EvenThread(num);
new Thread(oddThread,"ODD").start();
try {
Thread.sleep(400);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(evenThread,"EVEN").start();
}
}
/**
* 输出奇数
*/
class OddThread implements Runnable{
private Num num;
public OddThread(Num num){
this.num = num;
}
@Override
public void run() {
while (num.num < 100){
synchronized (num){
if(!num.isOdd()){
try {
num.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+":num ="+num.num++);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
num.notify();
}
}
}
}
/**
* 输出偶数
*/
class EvenThread implements Runnable {
private Num num;
public EvenThread(Num num) {
this.num = num;
}
@Override
public void run() {
while (num.num < 100) {
synchronized (num) {
if (!num.isEven()) {
try {
num.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+":"+num.num++);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
num.notify();
}
}
}
}
这种写法体现了 线程通信的精髓,符合条件则直接输出,不符合条件则 wait 等待另外线程 notify ,交替进行。
3,使用 ReentrantLock
理论上来说只要 wait notify 能实现的线程间通信,ReentrantLock 都能实现,代码如下:
package oddeven;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* ReentrantLock
* 推荐使用
*/
public class PrintOddEvenUseReentrantLock {
public static void main(String[] args) throws InterruptedException {
NumShare numShare = new NumShare();
OddPrint oddPrint = new OddPrint(numShare);
EvenPrint evenPrint = new EvenPrint(numShare);
new Thread(oddPrint, "odd").start();
Thread.sleep(400);
new Thread(evenPrint, "even").start();
}
}
class NumShare {
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public int num;
/**
* 是否是偶数
*
* @return
*/
public boolean isEven() {
return (num & 1) == 0;
}
/**
* 是否是奇数
*
* @return
*/
public boolean isOdd() {
return (num & 1) == 1;
}
}
/**
* 打印奇数
*/
class OddPrint implements Runnable {
private NumShare share;
public OddPrint(NumShare numShare) {
this.share = numShare;
}
@Override
public void run() {
while (share.num < 100) {
share.lock.lock();
try {
if (!share.isOdd()) {
share.condition.await();
}
System.out.println(Thread.currentThread().getName() + ":num=" + share.num++);
Thread.sleep(200);
share.condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
share.lock.unlock();
}
}
}
}
/**
* 打印奇数
*/
class EvenPrint implements Runnable {
private NumShare share;
public EvenPrint(NumShare numShare) {
this.share = numShare;
}
@Override
public void run() {
while (share.num < 100) {
share.lock.lock();
try {
if (!share.isEven()) {
share.condition.await();
}
System.out.println(Thread.currentThread().getName() + ":num=" + share.num++);
Thread.sleep(200);
share.condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
share.lock.unlock();
}
}
}
}
和 wait notify 是同样的思想,Lock 的通信的方式会更加直观一些。
4,使用 LinkedTransferQueue,代码如下:
package oddeven;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TransferQueue;
/**
* LinkedTransferQueue
*/
public class PrintOddEvenUseQueue {
public static void main(String[] args) {
NumQueue numQueue = new NumQueue();
OddQueue oddQueue = new OddQueue(numQueue);
EvenQueue evenQueue = new EvenQueue(numQueue);
new Thread(oddQueue, "Odd").start();
new Thread(evenQueue, "EVEN").start();
}
}
class NumQueue {
TransferQueue<Integer> queue = new LinkedTransferQueue<>();
public int num;
/**
* 是否是偶数
*
* @return
*/
public boolean isEven() {
return (num & 1) == 0;
}
/**
* 是否是奇数
*
* @return
*/
public boolean isOdd() {
return (num & 1) == 1;
}
}
/**
* 打印奇数
*/
class OddQueue implements Runnable {
private NumQueue numQueue;
public OddQueue(NumQueue numQueue) {
this.numQueue = numQueue;
}
@Override
public void run() {
while (numQueue.num < 100) {
if (!numQueue.isOdd()) {
try {
numQueue.queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ":num=" + numQueue.num++);
try {
Thread.sleep(200);
numQueue.queue.transfer(numQueue.num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 打印奇数
*/
class EvenQueue implements Runnable {
private NumQueue numQueue;
public EvenQueue(NumQueue numQueue) {
this.numQueue = numQueue;
}
@Override
public void run() {
while (numQueue.num < 100) {
if (!numQueue.isEven()) {
try {
numQueue.queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ":num=" + numQueue.num++);
try {
Thread.sleep(200);
numQueue.queue.transfer(numQueue.num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
线程间通信比较推荐2,3两种,比较常见。