前记:师夷长技以自强
1.用轮询实现的线程间通信机制
如下:
import java.util.ArrayList;
import java.util.List;
class MyList{
private List list = new ArrayList();
public void add(){
list.add("haha");
}
public int size(){
return list.size();
}
}
class ThreadA extends Thread{
volatile private MyList list;
public ThreadA(MyList list) {
this.list = list;
}
@Override
public void run() {
try{
for (int i = 0; i < 10; i++) {
list.add();
System.out.println("添加了"+(i+1)+"个元素");
Thread.sleep(1000);
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
class ThreadB extends Thread{
volatile private MyList list;
public ThreadB(MyList list) {
this.list = list;
}
@Override
public void run() {
try{
while (true){
if(list.size()==5){
System.out.println("==5,ThreadB exits!!!");
throw new InterruptedException();
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public class Test {
public static void main(String[] args) {
MyList myList = new MyList();
ThreadA a = new ThreadA(myList);
a.setName("A");
a.start();
ThreadB b = new ThreadB(myList);
b.setName("B");
b.start();
}
}
output:
添加了1个元素
添加了2个元素
添加了3个元素
添加了4个元素
添加了5个元素
==5,ThreadB exits!!!
java.lang.InterruptedException
at ThreadB.run(Test.java:48)
添加了6个元素
添加了7个元素
添加了8个元素
添加了9个元素
添加了10个元素
在线程A添加了5个元素后,线程B检测到了符合的条件然后抛出异常退出。然而,在这种情况下线程B是没有让出CPU在等待的。
2.wait和notify
2.1基本使用
wait函数是让当前线程暂停运行,notify函数是使等待当前锁的线程重新获得cpu运行。需要注意的是,wait和notify在被调用之前都要获取到相应的锁,也就是和如下:
class MyThread1 extends Thread{
private Object lock;
public MyThread1(Object lock) {
this.lock = lock;
}
@Override
public void run() {
try {
synchronized (lock){
System.out.println("start wait time="+System.currentTimeMillis());
lock.wait();
System.out.println("end wait time="+System.currentTimeMillis());
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
class MyThread2 extends Thread{
private Object lock;
public MyThread2(Object lock) {
this.lock = lock;
}
@Override
public void run() {
synchronized (lock){
System.out.println("start notify time="+System.currentTimeMillis());
lock.notify();
System.out.println("end notify time="+System.currentTimeMillis());
}
}
}
public class Test {
public static void main(String[] args) {
try{
Object lock = new Object();
MyThread1 t1 = new MyThread1(lock);
t1.start();
Thread.sleep(3000);
MyThread2 t2 = new MyThread2(lock);
t2.start();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
output:
start wait time=1591601206518
start notify time=1591601209523
end notify time=1591601209526
end wait time=1591601209528
可以看到,线程1停止三分钟后被线程2通知后继续运行。
2.2把第一个案例改为wait和notify实现
import java.util.ArrayList;
import java.util.List;
class MyList{
private static List list = new ArrayList();
public static void add(){
list.add("haha");
}
public static int size(){
return list.size();
}
}
class ThreadA extends Thread{
private Object lock;
public ThreadA(Object lock) {
this.lock = lock;
}
@Override
public void run() {
try{
synchronized (lock){
//if(MyList.size()!=5){
System.out.println("wait begin "+System.currentTimeMillis());
lock.wait();
System.out.println("wait end"+System.currentTimeMillis());
// }
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
class ThreadB extends Thread{
private Object lock;
public ThreadB(Object lock) {
this.lock = lock;
}
@Override
public void run() {
try{
synchronized (lock){
for (int i = 0; i < 10; i++) {
MyList.add();
if (MyList.size()==5){
lock.notify();
System.out.println("sended message");
}
System.out.println("add "+(i+1)+"elements");
Thread.sleep(1000);
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public class Test{
public static void main(String[] args) {
try{
Object lock = new Object();
ThreadA a = new ThreadA(lock);
a.start();
Thread.sleep(50);
ThreadB b = new ThreadB(lock);
b.start();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
output:
wait begin 1591604846284
add 1elements
add 2elements
add 3elements
add 4elements
sended message
add 5elements
add 6elements
add 7elements
add 8elements
add 9elements
add 10elements
wait end1591604856392
线程A刚开始获取锁然后调用wait释放锁,线程B得到锁运行,并在添加第5个元素的时候唤醒了线程A,从而得到以上的运行效果。处理notify方法外,还有一个notifyAll方法。前者是仅仅通知等待队列中的一个线程,后者是通知因同一个资源而进入等待队列的进程,然后根据优先级选择。
每个锁对象都有两个队列,一个是就绪队列,一个是阻塞队列。就绪队列存储了将要获得锁的线程,阻塞队列存储了被阻塞的线程。线程被唤醒进入就绪队列准备被执行调度,线程被wait则进入阻塞队列等待下一次的唤醒。
2.3wait释放锁而notify不释放锁
wait被调用后当前线程将释放锁,但是notify将不会释放,线程执行完同步代码块后才释放锁。
2.4 线程调用了wait方法后,再调用interruput方法会出现InterrruptedException异常
class Service{
public void testMethod(Object lock){
try {
synchronized (lock){
System.out.println("begin wait()");
lock.wait();
System.out.println("end wait");
}
}catch (InterruptedException e){
e.printStackTrace();
System.out.println("wait thread interrupted!!!");
}
}
}
class ThreadA extends Thread{
private Object lock;
public ThreadA(Object lock) {
this.lock = lock;
}
@Override
public void run() {
Service service = new Service();
service.testMethod(lock);
}
}
public class Test {
public static void main(String[] args) {
try{
Object lock = new Object();
ThreadA a = new ThreadA(lock);
a.start();
Thread.sleep(5000);
a.interrupt();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
output:
begin wait()
java.lang.InterruptedException
wait thread interrupted!!!
at java.base/java.lang.Object.wait(Native Method)
at java.base/java.lang.Object.wait(Object.java:516)
at Service.testMethod(Test.java:6)
at ThreadA.run(Test.java:26)
可见,线程A调用了wait方法后如果执行interrupt方法,就会引发InterruptedException异常。
2.5 notify只通知一个线程
class Service{
public void testMethod(Object lock){
try {
synchronized (lock){
System.out.println("begin wait() ThreadName="+Thread.currentThread().getName());
lock.wait();
System.out.println(" end wait() ThreadName="+Thread.currentThread().getName());
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
class ThreadA extends Thread{
private Object lock;
public ThreadA(Object lock) {
this.lock = lock;
}
@Override
public void run() {
Service service = new Service();
service.testMethod(lock);
}
}
class ThreadB extends Thread{
private Object lock;
public ThreadB(Object lock) {
this.lock = lock;
}
@Override
public void run() {
Service service = new Service();
service.testMethod(lock);
}
}
class ThreadC extends Thread{
private Object lock;
public ThreadC(Object lock) {
this.lock = lock;
}
@Override
public void run() {
Service service = new Service();
service.testMethod(lock);
}
}
class NotifyThread extends Thread{
private Object lock;
public NotifyThread(Object lock) {
this.lock = lock;
}
@Override
public void run() {
synchronized (lock){
lock.notify();
}
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
ThreadA a = new ThreadA(lock);
a.start();
ThreadB b = new ThreadB(lock);
b.start();
ThreadC c = new ThreadC(lock);
c.start();
Thread.sleep(1000);
NotifyThread notifyThread = new NotifyThread(lock);
notifyThread.start();
}
}
output:
begin wait() ThreadName=Thread-0
begin wait() ThreadName=Thread-1
begin wait() ThreadName=Thread-2
end wait() ThreadName=Thread-0
如果在把通知线程的代码改为如下
class NotifyThread extends Thread{
private Object lock;
public NotifyThread(Object lock) {
this.lock = lock;
}
@Override
public void run() {
synchronized (lock){
lock.notify();
lock.notify();
lock.notify();
lock.notify();
lock.notify();
lock.notify();
}
}
}
output:
begin wait() ThreadName=Thread-0
begin wait() ThreadName=Thread-1
begin wait() ThreadName=Thread-2
end wait() ThreadName=Thread-0
end wait() ThreadName=Thread-2
end wait() ThreadName=Thread-1
2.6 notifyAll唤醒所有线程
把上例的通知线程改为如下即可
class NotifyThread extends Thread{
private Object lock;
public NotifyThread(Object lock) {
this.lock = lock;
}
@Override
public void run() {
synchronized (lock){
lock.notifyAll();
}
}
}
output:
begin wait() ThreadName=Thread-1
begin wait() ThreadName=Thread-0
begin wait() ThreadName=Thread-2
end wait() ThreadName=Thread-1
end wait() ThreadName=Thread-2
end wait() ThreadName=Thread-0
2.7 wait(long)
如果在指定的时间内没有线程唤醒当前线程,则超过这个时间则自动唤醒。
class MyRunnable{
static private Object lock = new Object();
static private Runnable runnable1 = new Runnable() {
@Override
public void run() {
try{
synchronized (lock){
System.out.println("wait begin timer="+System.currentTimeMillis());
lock.wait(5000);
System.out.println("wait end timer="+System.currentTimeMillis());
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
};
public static void main(String[] args) {
Thread thread = new Thread(runnable1);
thread.start();
}
}
output:
wait begin timer=1591623207205
wait end timer=1591623212285
2.8wait/notify模式注意点
notify提前如果其他线程的notify比当前线程更早,则notify是无效的。
wait等待的条件发生变化时我们知道wait线程可以被唤醒,也可以超时自动唤醒,那当线程被唤醒后其所等待的添加发生变化时也会引起异常。比如两个线程同时删除一个集合的元素,开始集合为空两个删除线程都阻塞。后来另一个线程往集合放一个元素,会造成两个线程执行删除,而因为只有一个元素则必定会引发异常。
3 生产者消费者模式
3.1当只有一个生产者和消费者时
此时只要notify就可以完成对异类线程的唤醒。如下:
class P {
private String lock;
public P(String lock) {
this.lock = lock;
}
public void setValue() {
try {
synchronized (lock) {
while (!ValueObject.value.equals("")) {
System.out.println("P "+Thread.currentThread().getName()+" waiting!");
lock.wait();
}
System.out.println("P "+Thread.currentThread().getName()+" runnable!");
String value = System.currentTimeMillis() + "_" + System.nanoTime();
ValueObject.value = value;
lock.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class C {
private String lock;
public C(String lock) {
this.lock = lock;
}
public void getValue() {
try {
synchronized (lock) {
while (ValueObject.value.equals("")) {
System.out.println("C "+Thread.currentThread().getName()+" waiting!");
lock.wait();
}
System.out.println("C "+Thread.currentThread().getName()+" runnable!");
ValueObject.value = "";
lock.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ValueObject {
public static String value = "";
}
class ThreadP extends Thread {
private P p;
public ThreadP(P p) {
this.p = p;
}
@Override
public void run() {
while (true) {
p.setValue();
}
}
}
class ThreadC extends Thread {
private C r;
public ThreadC(C r) {
this.r = r;
}
@Override
public void run() {
while (true) {
r.getValue();
}
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
String lock = new String("");
P p = new P(lock);
C c = new C(lock);
ThreadP[] pThread = new ThreadP[2];
ThreadC[] cThread = new ThreadC[2];
for (int i = 0; i < 1; i++) {
pThread[i] = new ThreadP(p);
pThread[i].setName("P"+(i+1));
cThread[i] = new ThreadC(c);
cThread[i].setName("C"+(i+1));
pThread[i].start();
cThread[i].start();
}
Thread.sleep(5000);
Thread[] threadArray = new Thread[Thread.currentThread().getThreadGroup().activeCount()];
Thread.currentThread().getThreadGroup().enumerate(threadArray);
for (int i = 0; i < threadArray.length; i++) {
System.out.println(threadArray[i].getName()+" "+threadArray[i].getState());
}
}
}
output:(部分)
P P1 waiting!
C C1 runnable!
P P1 runnable!
P P1 waiting!
C C1 runnable!
P P1 runnable!
P P1 waiting!
C C1 runnable!
可以看出,生产者和消费者可以同步进行。
3.2多生产者和多消费者时
如果每次仅仅唤醒一个线程,那么可能只唤醒了同类线程,等待的线程越来越多,最后造成程序的假死。
class P {
private String lock;
public P(String lock) {
this.lock = lock;
}
public void setValue() {
try {
synchronized (lock) {
while (!ValueObject.value.equals("")) {
System.out.println("P "+Thread.currentThread().getName()+" waiting!");
lock.wait();
}
System.out.println("P "+Thread.currentThread().getName()+" runnable!");
String value = System.currentTimeMillis() + "_" + System.nanoTime();
ValueObject.value = value;
lock.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class C {
private String lock;
public C(String lock) {
this.lock = lock;
}
public void getValue() {
try {
synchronized (lock) {
while (ValueObject.value.equals("")) {
System.out.println("C "+Thread.currentThread().getName()+" waiting!");
lock.wait();
}
System.out.println("C "+Thread.currentThread().getName()+" runnable!");
ValueObject.value = "";
lock.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ValueObject {
public static String value = "";
}
class ThreadP extends Thread {
private P p;
public ThreadP(P p) {
this.p = p;
}
@Override
public void run() {
while (true) {
p.setValue();
}
}
}
class ThreadC extends Thread {
private C r;
public ThreadC(C r) {
this.r = r;
}
@Override
public void run() {
while (true) {
r.getValue();
}
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
String lock = new String("");
P p = new P(lock);
C c = new C(lock);
ThreadP[] pThread = new ThreadP[2];
ThreadC[] cThread = new ThreadC[2];
for (int i = 0; i < 2; i++) {
pThread[i] = new ThreadP(p);
pThread[i].setName("P"+(i+1));
cThread[i] = new ThreadC(c);
cThread[i].setName("C"+(i+1));
pThread[i].start();
cThread[i].start();
}
Thread.sleep(5000);
Thread[] threadArray = new Thread[Thread.currentThread().getThreadGroup().activeCount()];
Thread.currentThread().getThreadGroup().enumerate(threadArray);
for (int i = 0; i < threadArray.length; i++) {
System.out.println(threadArray[i].getName()+" "+threadArray[i].getState());
}
}
}
output:(部分)
P P1 waiting!
C C2 runnable!
C C2 waiting!
P P1 runnable!
P P1 waiting!
C C2 runnable!
C C2 waiting!
P P1 runnable!
P P1 waiting!
C C2 runnable!
C C2 waiting!
P P1 runnable!
C C2 runnable!
P P1 runnable!
P P1 waiting!
P P2 waiting!
C C1 runnable!
C C1 waiting!
P P1 runnable!
P P1 waiting!
P P2 waiting!
C C2 runnable!
C C2 waiting!
C C1 waiting!
main RUNNABLE
Monitor Ctrl-Break RUNNABLE
P1 WAITING
C1 WAITING
P2 WAITING
C2 WAITING
经过若干次的调用,最后所有的生产者和消费者都进入了等待状态。解决的办法也很简单,就是把P.java和C.java中的notify改为notifyAll。
4.管道通信
4.1字节流
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
class WriteData{
public void writeMethod(PipedOutputStream out){
try{
System.out.println("write :");
for (int i = 0; i < 300; i++) {
String outData = "" + (i+1);
out.write(outData.getBytes());
System.out.print(outData);
}
System.out.println();
out.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
class ReadData{
public void readMethod(PipedInputStream input){
try{
System.out.println("read :");
byte[] byteArray = new byte[20];
int readLength = input.read(byteArray);
while (readLength != -1){
String newData = new String(byteArray, 0, readLength);
System.out.print(newData);
readLength = input.read(byteArray);
}
System.out.println();
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class ThreadWrite extends Thread{
private WriteData write;
private PipedOutputStream out;
public ThreadWrite(WriteData write, PipedOutputStream out) {
this.write = write;
this.out = out;
}
@Override
public void run() {
write.writeMethod(out);
}
}
class ThreadRead extends Thread{
private ReadData read;
private PipedInputStream input;
public ThreadRead(ReadData read, PipedInputStream input) {
this.read = read;
this.input = input;
}
@Override
public void run() {
read.readMethod(input);
}
}
public class Test{
public static void main(String[] args) {
try {
WriteData writeData = new WriteData();
ReadData readData = new ReadData();
PipedInputStream inputStream = new PipedInputStream();
PipedOutputStream outputStream = new PipedOutputStream();
// outputStream.connect(inputStream);
inputStream.connect(outputStream);
ThreadRead threadRead = new ThreadRead(readData, inputStream);
threadRead.start();
Thread.sleep(2000);
ThreadWrite threadWrite = new ThreadWrite(writeData, outputStream);
threadWrite.start();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
4.2字符流
import java.io.*;
class WriteData{
public void writeMethod(PipedWriter out){
try{
System.out.println("write :");
for (int i = 0; i < 300; i++) {
String outData = "" + (i+1);
out.write(outData);
System.out.print(outData);
}
System.out.println();
out.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
class ReadData{
public void readMethod(PipedReader input){
try{
System.out.println("read :");
char[] byteArray = new char[20];
int readLength = input.read(byteArray);
while (readLength != -1){
String newData = new String(byteArray, 0, readLength);
System.out.print(newData);
readLength = input.read(byteArray);
}
System.out.println();
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class ThreadWrite extends Thread{
private WriteData write;
private PipedWriter out;
public ThreadWrite(WriteData write, PipedWriter out) {
this.write = write;
this.out = out;
}
@Override
public void run() {
write.writeMethod(out);
}
}
class ThreadRead extends Thread{
private ReadData read;
private PipedReader input;
public ThreadRead(ReadData read, PipedReader input) {
this.read = read;
this.input = input;
}
@Override
public void run() {
read.readMethod(input);
}
}
public class Test{
public static void main(String[] args) {
try {
WriteData writeData = new WriteData();
ReadData readData = new ReadData();
PipedReader inputStream = new PipedReader();
PipedWriter outputStream = new PipedWriter();
// outputStream.connect(inputStream);
inputStream.connect(outputStream);
ThreadRead threadRead = new ThreadRead(readData, inputStream);
threadRead.start();
Thread.sleep(2000);
ThreadWrite threadWrite = new ThreadWrite(writeData, outputStream);
threadWrite.start();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
5.join方法
5.1join方法的基本使用
join方法使所属的线程对象x正常执行run方法中的任务,使当前线程z进行无限期阻塞,等待线程x销毁后再继续执行线程z后面的代码。
class MyThread extends Thread{
@Override
public void run() {
try{
int secondValue = (int) (Math.random() * 10000);
System.out.println(secondValue);
Thread.sleep(secondValue);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Test{
public static void main(String[] args) {
try {
MyThread threadTest = new MyThread();
threadTest.start();
threadTest.join();
System.out.println("haha");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
output:
4603
haha
5.2 join方法与interrrupt
class ThreadA extends Thread{
@Override
public void run() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
String newString = new String();
Math.random();
}
}
}
class ThreadB extends Thread{
@Override
public void run() {
try {
ThreadA threadA = new ThreadA();
threadA.start();
threadA.join();
System.out.println("ThreadB run end!");
} catch (InterruptedException e) {
System.out.println("ThreadB run catch!");
e.printStackTrace();
}
}
}
class ThreadC extends Thread{
private ThreadB threadB;
public ThreadC(ThreadB threadB) {
this.threadB = threadB;
}
@Override
public void run() {
threadB.interrupt();
}
}
public class Test{
public static void main(String[] args) {
try {
ThreadB b = new ThreadB();
b.start();
Thread.sleep(500);
ThreadC c = new ThreadC(b);
c.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
output:
ThreadB run catch!
java.lang.InterruptedException
at java.base/java.lang.Object.wait(Native Method)
at java.base/java.lang.Thread.join(Thread.java:1353)
at java.base/java.lang.Thread.join(Thread.java:1427)
at ThreadB.run(Test.java:17)
当线程B在调用了join后被线程C interrupt,会发生异常,但线程按钮还是呈“红色”,因为线程A还在继续运行中,没有发生异常。
5.3 join(long)方法
class MyThread extends Thread{
@Override
public void run() {
try {
System.out.println("begin Timer="+System.currentTimeMillis());
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Test{
public static void main(String[] args) {
try {
MyThread threadTest = new MyThread();
threadTest.start();
threadTest.join(2000);
System.out.println(" end timer="+System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
output:
begin Timer=1591694019386
end timer=1591694021385
可以看到,运行的效果是等待了2秒钟。
5.4join(long)和sleep(join)的区别
阅读join的源码发现里面是使用wait实现的,因此join别调用后是可以释放锁的。而sleep方法被调用后却不释放锁。
class ThreadA extends Thread{
private ThreadB b;
public ThreadA(ThreadB b) {
this.b = b;
}
@Override
public void run() {
try {
synchronized (b){
b.start();
Thread.sleep(6000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ThreadB extends Thread{
@Override
public void run() {
try {
System.out.println(" b run begin timer="+System.currentTimeMillis());
Thread.sleep(5000);
System.out.println(" b run end timer="+System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized public void bService(){
System.out.println("bService timer"+System.currentTimeMillis());
}
}
class ThreadC extends Thread{
private ThreadB threadB;
public ThreadC(ThreadB threadB) {
this.threadB = threadB;
}
@Override
public void run() {
threadB.bService();
}
}
public class Test{
public static void main(String[] args) {
try {
ThreadB b = new ThreadB();
ThreadA a = new ThreadA(b);
a.start();
Thread.sleep(1000);
ThreadC c = new ThreadC(b);
c.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
output:
b run begin timer=1591706030617
b run end timer=1591706035720
bService timer1591706036618
可见,bService是过了6秒后被调用的,也就是说线程A在调用了Thread.sleep(6000)后没有释放线程B对象的锁,导致其同步方法bService不能被线程C立即调用。而join是可以释放锁的,如下
class ThreadA extends Thread{
private ThreadB b;
public ThreadA(ThreadB b) {
this.b = b;
}
@Override
public void run() {
try {
synchronized (b){
b.start();
b.join();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ThreadB extends Thread{
@Override
public void run() {
try {
System.out.println(" b run begin timer="+System.currentTimeMillis());
Thread.sleep(5000);
System.out.println(" b run end timer="+System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized public void bService(){
System.out.println("bService timer"+System.currentTimeMillis());
}
}
class ThreadC extends Thread{
private ThreadB threadB;
public ThreadC(ThreadB threadB) {
this.threadB = threadB;
}
@Override
public void run() {
threadB.bService();
}
}
public class Test{
public static void main(String[] args) {
try {
ThreadB b = new ThreadB();
ThreadA a = new ThreadA(b);
a.start();
Thread.sleep(1000);
ThreadC c = new ThreadC(b);
c.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
output:
b run begin timer=1591706475496
bService timer1591706476497
b run end timer=1591706480580
6.ThreadLocal使用
ThreadLocal类主要解决每个线程绑定自己的值,变量在不同线程间的隔离性。
public class Test{
public static ThreadLocal t1 = new ThreadLocal();
public static void main(String[] args) {
if(t1.get() == null){
System.out.println("no value!");
t1.set("my value");
}
System.out.println(t1.get());
System.out.println(t1.get());
}
}
output:
no value!
my value
my value
可以通过set和get对ThreadLocal操作。
6.1 验证线程变量的隔离性
class Tools{
public static ThreadLocal t1 = new ThreadLocal();
}
class ThreadA extends Thread{
@Override
public void run() {
try {
for (int i = 0; i < 100; i++) {
Tools.t1.set("ThreadA"+i);
System.out.println("ThreadA get Value="+Tools.t1.get());
Thread.sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ThreadB extends Thread{
@Override
public void run() {
try {
for (int i = 0; i < 100; i++) {
Tools.t1.set("ThreadB"+i);
System.out.println("ThreadB get Value="+Tools.t1.get());
Thread.sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Test{
public static void main(String[] args) {
try {
ThreadA a = new ThreadA();
ThreadB b = new ThreadB();
a.start();
b.start();
for (int i = 0; i < 100; i++) {
Tools.t1.set("Main"+i);
System.out.println("Main get Value="+Tools.t1.get());
Thread.sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
output:(部分)
Main get Value=Main92
ThreadA get Value=ThreadA93
ThreadB get Value=ThreadB93
Main get Value=Main93
ThreadA get Value=ThreadA94
ThreadB get Value=ThreadB94
Main get Value=Main94
ThreadA get Value=ThreadA95
ThreadB get Value=ThreadB95
Main get Value=Main95
ThreadA get Value=ThreadA96
ThreadB get Value=ThreadB96
Main get Value=Main96
ThreadA get Value=ThreadA97
ThreadB get Value=ThreadB97
Main get Value=Main97
ThreadA get Value=ThreadA98
ThreadB get Value=ThreadB98
Main get Value=Main98
ThreadA get Value=ThreadA99
ThreadB get Value=ThreadB99
Main get Value=Main99
可见,这三个线程都能取出属于自己的值,ThreadLocal类对每个线程的存储是具有隔离性的。
6.2解决ThreadLocal中值为null
可以创建一个继承自ThreadLocal的类ThreadLocalEx,重写其initialValue方法
import java.util.Date;
class ThreadLocalEx extends ThreadLocal{
@Override
protected Object initialValue() {
return new Date().getTime();
}
}
class Tools{
public static ThreadLocalEx t1 = new ThreadLocalEx();
}
class ThreadA extends Thread{
@Override
public void run() {
try{
for (int i = 0; i < 10; i++) {
System.out.println("ThreadA value="+Tools.t1.get());
Thread.sleep(100);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Test{
public static void main(String[] args) {
try {
for (int i = 0; i < 10; i++) {
System.out.println("Main value="+Tools.t1.get());
Thread.sleep(100);
}
Thread.sleep(5000);
ThreadA a = new ThreadA();
a.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
output:
Main value=1591710156676
Main value=1591710156676
Main value=1591710156676
Main value=1591710156676
Main value=1591710156676
Main value=1591710156676
Main value=1591710156676
Main value=1591710156676
Main value=1591710156676
Main value=1591710156676
ThreadA value=1591710162774
ThreadA value=1591710162774
ThreadA value=1591710162774
ThreadA value=1591710162774
ThreadA value=1591710162774
ThreadA value=1591710162774
ThreadA value=1591710162774
ThreadA value=1591710162774
ThreadA value=1591710162774
ThreadA value=1591710162774
可以看到,Main和ThreadA中都有自己的默认值了。
7.InheritableThreadLocal类
该类用法与ThreadLocal类似,只是子线程可以继承从父线程中的值。
8.总结
本文主要说明了线程之间是如何通信的,其中等待通知是基本的模式,还有生产者消费者,管道等通信方式,join方法的使用等。