线程自启动时,就拥有了自己的栈空间。然后会一直运行直到结束。多线程的目的是多条线程执行不同的逻辑业务从而能够提升业务整体的响应速度,如果线程仅仅是孤零零的执行,不同的逻辑业务就不能最终汇聚成一个完整的业务那么多线程也就失去了意义,这就是为什么要有线程间通信的存在。线程间的通信可以是主、子线程通信,也可以是子、子线程通信。
实现线程之间的通信有以下方法:
wait()
/notify()
是一种低级别的同步机制,适合需要精细控制的场合;BlockingQueue
和Exchanger
提供了更高层次的抽象,简化了线程间的数据交换;Locks
和Condition
提供了更灵活的锁机制,适合复杂的同步场景;Semaphore
则用于控制资源访问。
一、共享内存
线程之间可以通过共享变量来进行通信。不同的线程可以共享同一个变量,并在变量上进行读写操作。需要注意的是,共享变量可能会引发线程安全问题,需要通过同步机制来确保线程安全。
1、使用volatile 关键字
2、synchronized 关键字、Lock 锁
线程同步可以通过 synchronized 关键字和 Lock 锁来实现线程间的通信。
这种方式,本质上就是“共享内存”式的通信。多个线程需要访问同一个共享变量,谁拿到了锁(获得了访问权限),谁就可以执行。如:
// 共享对象
class MyObject {
synchronized public void methodA() {
//do something....
}
synchronized public void methodB() {
//do some other thing
}
}
// 线程 A
class ThreadA extends Thread {
private MyObject object;
//省略构造方法
@Override
public void run() {
super.run();
object.methodA();
}
}
// 线程 B
class ThreadB extends Thread {
private MyObject object;
//省略构造方法
@Override
public void run() {
super.run();
object.methodB();
}
}
// 主类
public class Main {
public static void main(String[] args) {
MyObject object = new MyObject();
//线程A与线程B 持有的是同一个对象:object
ThreadA a = new ThreadA(object);
ThreadB b = new ThreadB(object);
a.start();
b.start();
}
}
由于线程 A 和线程 B 持有同一个 MyObject 类的对象 object,尽管这两个线程需要调用不同的方法,但是它们是同步执行的,线程 B 需要等待线程 A 执行完了 methodA() 方法之后,它才能执行 methodB() 方法。这样,线程 A 和线程 B 就实现了 通信。
二、消息传递
1、等待/通知机制
1.1、介绍
在基于“锁”的方式中,线程需要不断地去尝试获得锁,如果失败了,再继续尝试,这可能会耗费服务器资源。而等待/通知机制是另一种方式。
一个线程修改一个对象的值,另一个线程感知变化。线程A调用对象O的wait()进入等待状态,另一个线程B调用对象O的notify()或者 notifuAll()方法,线程A 收到通知后从对象O 的wait()方法返回,执行后续操作。两个线程通过对象O来完成交互,而对象上的wait()和notify/notifyAll()的关系就如同开关信号一样,用来完成等待方和通知方之间的交互工作。
(1)wait();当前线程暂停,等待notify()来唤醒(释放资源)。
(2)使用锁对象的notify()方法可以将正在等待的线程唤醒,但是同时有多个线程都处于等待状态,notify()只是随机唤醒一个。
注:唤醒后的进程进入就绪态,而不是进入运行态。虽然线程被唤醒,但只有当前线程放弃对同步锁对象的锁定,被唤醒的线程才可能执行被执行
(3)notifyAll()
唤醒在此同步锁对象上等待的所有线程。同上,只有当前线程放弃对同步锁对象的锁定,才可能执行被唤醒的线程。
1.2、规则
1.2.1、等待方遵循原则
(1)获取对象的锁。
(2)如果条件不满足,那么调用对象的 wait
()方法,被通知后仍要检查条件。