概论:线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体。线程间的通信就是成为整体的比用方案之一,可以说,是线程间进行通信后,系统之间的交互性会更强大,在大大提高CPU利用率的同时还会使程序员对各线程任务在处理的过程中进行有效的把控与监督。
①等待/通知机制
1.不使用等待/通知机制实现线程间通信
示例代码:
public class MyList {
private volatile List list = new ArrayList();
public void add() {
list.add("kiven");
}
public int size() {
return list.size();
}
}
class ThreadA extends Thread {
private MyList list;
public ThreadA(MyList list) {
super();
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 (Exception e) {
}
}
}
class ThreadB extends Thread {
private MyList list;
public ThreadB(MyList list) {
super();
this.list = list;
}
@Override
public void run() {
try {
while (true) {
if (list.size() == 5) {
System.out.println("==5 了,线程b要退出了");
throw new InterruptedException();
}
}
} catch (Exception e) {
}
}
}
class Test {
public static void main(String[] args) {
MyList service = new MyList();
ThreadB b = new ThreadB(service);
b.setName("B");
b.start();
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
}
}
运行结果:虽然连个线程间实现了通信,但有一个弊端就是,线程ThreadB.java不停地通过while语句轮询机制来检测某一个条件,这样会浪费CPU资源。如果轮询的时间间隔很小,更浪费CPU资源;如果轮询的时间间隔很大,有可能会取不到想要得到的数据。所以就需要有一种机制来实现减少CPU的资源浪费,而且还可以实现在多个线程间通信,它就是“wait/nofity”机制。
2.等待/通知机制的实现
示例代码:
public class MyThread1 extends Thread {
private Object lock;
public MyThread1(Object lock) {
super();
this.lock = lock;
}
@Override
public void run() {
try {
synchronized (lock) {
System.out.println("开始 wait time=" + System.currentTimeMillis());
lock.wait();
System.out.println("结束 wait time=" + System.currentTimeMillis());
}
} catch (InterruptedException e) {
}
}
}
class MyThread2 extends Thread {
private Object lock;
public MyThread2(Object lock) {
this.lock = lock;
}
@Override
public void run() {
synchronized (lock) {
System.out.println("开始notify time=" + System.currentTimeMillis());
lock.notify();
System.out.println("结束notify time=" + System.currentTimeMillis());
}
}
}
class Test {
public static void main(String[] args) throws Exception {
Object lock = new Object();
MyThread1 myThread1 = new MyThread1(lock);
myThread1.start();
Thread.sleep(3000);
MyThread2 myThread2 = new MyThread2(lock);
myThread2.start();
}
}
运行结果:3秒后线程nofity通知唤醒。
3.使用wait()与notify()来实现前面size()值等于5的实验
示例代码:
public class MyList {
private static List list = new ArrayList();
public static void add() {
list.add("anyString");
}
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) {
}
}
}
class ThreadB extends Thread {
private Object lock;
public ThreadB(Object lock) {
super();
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("已发出通知!");
}
System.out.println("添加了" + (i + 1) + "个元素!");
Thread.sleep(1000);
}
}
} catch (InterruptedException e) {
}
}
}
class Test {
public static void main(String[] args) throws Exception {
Object lock = new Object();
ThreadA a = new ThreadA(lock);
a.start();
Thread.sleep(50);
ThreadB b = new ThreadB(lock);
b.start();
}
}
运行代码:wait end 在最后输出,这样说明nofity()方法执行后并不立即释放锁。
4.当interrupt方法遇到wait方法
示例代码:
public class Service {
public void testMethod(Object lock) {
try {
synchronized (lock) {
System.out.println("begin wati");
lock.wait();
System.out.println("end wait");
}
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("出现异常了,因为呈wait状态的线程被interrupt了!");
}
}
}
class ThreadA extends Thread {
private Object lock;
public ThreadA(Object lock) {
super();
this.lock = lock;
}
@Override
public void run() {
Service service = new Service();
service.testMethod(lock);
}
}
class Test {
public static void main(String[] args) {
try {
Object lock = new Object();
ThreadA a = new ThreadA(lock);
a.start();
Thread.sleep(3000);
a.interrupt();
} catch (Exception e) {
}
}
}
运行结果:
5.只通知一个线程
示例代码:
public class Service {
public void testMethod(Object lock) {
try {
synchronized (lock) {
System.out.println("begin wati() ThreadName=" + Thread.currentThread().getName());
lock.wait();
System.out.println("end wait() ThreadName=" + Thread.currentThread().getName());
}
} catch (Exception e) {
}
}
}
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) {
super();
this.lock = lock;
}
@Override
public void run() {
synchronized (lock){
lock.notify();
}
}
}
class Test {
public static void main(String[] args) throws Exception {
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();
}
}
运行结果:方法notify()仅随机唤醒一个线程,使用nofityAll()可以唤醒所有线程
6.方法wait(long)的使用
--带一个参数的wait(long)方法的功能是等待某一时间内是否有线程对锁进行唤醒,如果超过这个时间则自动唤醒。
②生产者/消费者模式实现
1.一生产与一消费:操作值
示例代码:
public class P {
private String lock;
public P(String lock) {
this.lock = lock;
}
public void setValue() {
try {
synchronized (lock) {
if (!ValueObject.value.equals("")) {
lock.wait();
}
String value = System.currentTimeMillis() + "_" + System.nanoTime();
System.out.println("set的值是" + value);
ValueObject.value = value;
lock.notify();
}
} catch (Exception e) {
}
}
}
class C {
private String lock;
public C(String lock) {
super();
this.lock = lock;
}
public void getValue() {
try {
synchronized (lock) {
if (ValueObject.value.equals("")) {
lock.wait();
}
System.out.println("get的值是" + ValueObject.value);
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) {
super();
this.p = p;
}
@Override
public void run() {
while (true) {
p.setValue();
}
}
}
class ThreadC extends Thread {
private C c;
public ThreadC(C c) {
super();
this.c = c;
}
@Override
public void run() {
while (true) {
c.getValue();
}
}
}
class Test {
public static void main(String[] args) {
String lock = new String("");
P p = new P(lock);
C c = new C(lock);
ThreadP pthread = new ThreadP(p);
ThreadC cthread = new ThreadC(c);
pthread.start();
cthread.start();
}
}
运行结果:
③方法join的使用
1.引入join铺垫
示例代码:
public 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) {
}
}
}
class Test {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
//Thread.sleep(?)
System.out.println("我想当threadTest对象执行完毕后我再执行");
System.out.println("但上面代码中的sleep()中的值应该写多少呢?");
System.out.println("答案是:根据不能确定:");
}
}
运行结果:
2.用join()方法来解决
示例代码:
public 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) {
}
}
}
class Test {
public static void main(String[] args) throws InterruptedException {
MyThread thread = new MyThread();
thread.start();
thread.join();
System.out.println("我想threadTest对象执行完毕后我再执行,我做到了");
}
}
运行结果:
3.方法join()如果和interrupt()方法遇到,则会出现异常。
4.方法join(long)与sleep(long)的区别:join(long)具有释放锁的特点,sleep(long)不释放锁。
④类ThreadLocal的使用
1.验证线程变量的隔离性
示例代码:
public 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 + 1));
System.out.println("ThreadA get Value=" + Tools.t1.get());
Thread.sleep(200);
}
} catch (Exception e) {
}
}
}
class ThreadB extends Thread {
@Override
public void run() {
try {
for (int i = 0; i < 100; i++) {
Tools.t1.set("ThreadB" + (i + 1));
System.out.println("ThreadB get Value=" + Tools.t1.get());
Thread.sleep(200);
}
} catch (Exception e) {
}
}
}
class Run {
public static void main(String[] args) throws Exception {
ThreadA a = new ThreadA();
ThreadB b = new ThreadB();
a.start();
b.start();
for (int i = 0; i < 100; i++) {
Tools.t1.set("Main" + (i + 1));
System.out.println("Main get Value=" + Tools.t1.get());
Thread.sleep(200);
}
}
}
运行结果:虽然3个线程都向t1对象中set()数据值,但每个线程还是能取出自己的数据。
2.使用InheritableThreadLocal可以在子线程中取得父线程继承下来的值
示例代码:
public class InheritableThreadLocalExt extends InheritableThreadLocal {
@Override
protected Object initialValue() {
return new Date().getTime();
}
@Override
protected Object childValue(Object parentValue) {
return parentValue + "我在子线程加的~!";
}
}
class Tools {
public static InheritableThreadLocalExt t1 = new InheritableThreadLocalExt();
}
class ThreadA extends Thread {
@Override
public void run() {
try {
for (int i = 0; i < 10; i++) {
System.out.println("在ThreadA线程中取得值=" + Tools.t1.get());
Thread.sleep(100);
}
} catch (Exception e) {
}
}
}
class Run {
public static void main(String[] args) throws Exception {
for (int i = 0; i < 10; i++) {
System.out.println(" 在Main线程中取值=" + Tools.t1.get());
Thread.sleep(100);
}
Thread.sleep(3000);
ThreadA a = new ThreadA();
a.start();
}
}
运行结果:参考:《Java多线程编程核心技术》--机械工业出版社 高洪岩 著