一、wait/notify机制
(拥有相同锁的线程才可以实现wait/notify机制)
原理
wait():是Object类的方法,它的作用是使当前执行wait()方法的线程等待,在wait()所在的代码行处暂停执行,并释放锁,直到接到通知或中断为止。在执行wait()之前,线程必须获得该对象的对象级锁,即只能在同步方法或同步代码块中调用wait()。(调用wait()若没有持有锁,则抛出IlegalMonitorStateException)
notify():同样需要在同步方法或同步代码块中调用。 该方法用来通知那些可能等待该锁的线程(若有多个线程等待,按wait()方法执行顺序进行通知)。执行notify()后,不会马上释放该锁,要在线程将程序执行完(退出synchronized区域)才会释放。
notifyAll()
唤醒全部线程
需要注意的是:唤醒的顺序取决于JVM的实现。
wait(long time)
等待指定时间time后,接着往下执行
想要自动往下接着执行的话也需要持有锁,如果没有锁的话就会一直等待
线程的状态切换
JDK8中对于Thread状态的枚举定义,所有的状态如下所示:
NEW
RUNNABLE
BLOCKED
WAITING
TIMED_WAITING
TERMINATED
1)NEW
一个已经创建的线程,但是还没有调用start方法启动的线程所处的状态
2)RUNNABLE
该状态包含两种可能。有可能正在运行,或者正在等待CPU资源。总体上就是当我们创建线程并且启动之后,就属于Runnable状态。
3)BLOCKED
阻塞状态,当线程准备进入synchronized同步块或同步方法的时候,需要申请一个监视器锁而进行的等待,会使线程进入BLOCKED状态。
4)WAITING
该状态的出现是因为调用了Object.wait()或者Thread.join()或者LockSupport.park()。处于该状态下的线程在等待另一个线程 执行一些其余action来将其唤醒。
5)TIMED_WAITING
该状态和上一个状态其实是一样的,是不过其等待的时间是明确的。
6)TERMINATED
消亡状态比较容易理解,那就是线程执行结束了,run方法执行结束表示线程处于消亡状态了
消费者/生产者模式的实现
一生产一消费:操作值
存储值的对象
public class ValueObject {
public static String value = "";
}
生产者
public class P {
private String lock;
public P(String lock) {
super();
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 is :" + value);
ValueObject.value = value;
lock.notify();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
消费者
public 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 is :" + ValueObject.value);
ValueObject.value = "";
lock.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
生产者线程
public class ThreadP extends Thread {
private P p;
public ThreadP(P p) {
super();
this.p = p;
}
@Override
public void run() {
while (true) {
p.setValue();
}
}
}
消费者线程
public class ThreadC extends Thread {
private C c;
public ThreadC(C c) {
super();
this.c = c;
}
@Override
public void run() {
while (true) {
c.getValue();
}
}
}
测试代码
public class Test {
public static void main(String[] args) {
String lock = new String("");
P p = new P(lock);
C c = new C(lock);
ThreadP threadP = new ThreadP(p);
ThreadC threadC = new ThreadC(c);
threadP.start();
threadC.start();
}
}
需要注意的是:当有多个生产者和多个消费者的时候,可能会出现“假死”(也就是所有的线程都呈waiting状态,不再执行任何任务)
因为notify()唤醒的未必是异类,可能生产者唤醒了生产者,消费者唤醒了消费者
解决假死:可将notify()改为notifyAll()
多生产多消费(一次生产/消费多个值)
创建容器(最大容量10)
public class Box {
private static List list = new ArrayList();
synchronized public void add() {
if (size() < 50) {
list.add("anything");
System.out.println("Thread: " + Thread.currentThread().getName() + "execute add(), " +
"the size is :" + size());
}
}
synchronized public Object popFirst() {
Object obj = list.remove(0);
System.out.println("Thread: " + Thread.currentThread().getName() + "execute popFirst(), " +
"the size is : " + size());
return obj;
}
synchronized public int size() {
return list.size();
}
}
生产者业务代码
public class SetService {
private Box box;
public SetService(Box box) {
super();
this.box = box;
}
public void setMethod() {
try {
synchronized (this) {
while (box.size() == 10) {
System.out.println("---------------");
this.wait();
}
Thread.sleep(300);
box.add();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void checkBoxStatus() {
try {
while (true) {
synchronized (this) {
if (box.size() < 50) {
this.notifyAll();
}
}
System.out.println("set checkbox = " + box.size());
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
生产者线程代码
public class SetValueThread extends Thread {
private SetService setService;
public SetValueThread(SetService setService) {
super();
this.setService = setService;
}
@Override
public void run() {
while (true) {
setService.setMethod();
}
}
}
生产者容器大小测试类
public class SetCheckThread extends Thread {
private SetService setService;
public SetCheckThread(SetService setService) {
super();
this.setService = setService;
}
@Override
public void run() {
setService.checkBoxStatus();
}
}
消费者业务代码
public class GetService {
private Box box;
public GetService(Box box) {
super();
this.box = box;
}
public void getMethod() {
try {
synchronized (this) {
while (box.size() == 0) {
System.out.println("||||||||||||||");
this.wait();
}
box.popFirst();
}
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void checkBoxStatus() {
try {
while (true) {
synchronized (this) {
if (box.size() > 0) {
this.notifyAll();
}
}
System.out.println("get checkbox = " + box.size());
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
消费者线程代码
public class GetValueThread extends Thread {
private GetService getService;
public GetValueThread(GetService getService) {
super();
this.getService = getService;
}
@Override
public void run() {
while (true) {
getService.getMethod();
}
}
}
消费者容器大小测试类
public class GetCheckThread extends Thread {
private GetService getService;
public GetCheckThread(GetService getService) {
super();
this.getService = getService;
}
@Override
public void run() {
getService.checkBoxStatus();
}
}
测试代码
public class Test {
public static void main(String[] args) throws InterruptedException {
Box box = new Box();
SetService setService = new SetService(box);
for (int i = 0; i < 2; i++) {
SetValueThread setValueThread = new SetValueThread(setService);
setValueThread.start();
}
Thread.sleep(50);
SetCheckThread setCheckThread = new SetCheckThread(setService);
setCheckThread.start();
Thread.sleep(10000);
GetService getService = new GetService(box);
for (int i = 0; i < 10; i++) {
GetValueThread getValueThread = new GetValueThread(getService);
getValueThread.start();
}
Thread.sleep(50);
GetCheckThread getCheckThread = new GetCheckThread(getService);
getCheckThread.start();
}
}
测试结果
通过管道进行线程间的通信
java语言提供了各种各样的输入/输出流,其中管道流是一种特殊的流,用在不同线程间直接传送数据。
字节流:PipedInputStream、PipedOutputStream
字符流:PipedReader、PipedWriter
join()方法
很多情况下,主线程创建并启动子线程,如果子线程要进行大量的耗时运算,主线程可能会在子线程结束之前结束,如果想要主线程等待子线程结束后再结束,那么此时就需要join()(内部使用了wait(),当前线程会释放锁)
作用:使所属线程对象A正常执行run()方法中的任务,而使当前线程B进入无限期的阻塞。等待A执行完销毁后再继续执行B后面的代码
join(long time):参数用于设定等待时间,不管使用该方法的线程是否执行完毕,时间到了就会去重新获得锁并接着执行。
join(long millis, int nanos):等待该线程终止的时长为 millis(毫秒) + nanos(纳秒)
类ThreadLocal
作用:将数据放入当前线程对象中的Map中,这个Map是Thread的实例变量。类ThreadLocal自己部管理、不存储任何数据,它只是数据和Map之间的桥梁,用于将数据放入Map中(每个线程的Map存有自己的数据,每个Thread的Map值对当前线程可见),如图
如果从未在Thread中的Map存储ThreadLocal对象对应的value值,则get()返回null
隔离性的验证
ThreadLocal变量
public class Tools {
public static ThreadLocal t1 = new ThreadLocal();
}
线程A
public class MyThreadA extends Thread {
@Override
public void run() {
try {
for (int i = 0; i < 10; i++) {
Tools.t1.set("A " + (i + 1));
System.out.println("A get " + Tools.t1.get());
int sleepValue = (int) (Math.random() * 1000);
Thread.sleep(sleepValue);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
线程B
public class MyThreadB extends Thread {
@Override
public void run() {
try {
for (int i = 0; i < 10; i++) {
Tools.t1.set("B " + (i + 1));
System.out.println(" B get " + Tools.t1.get());
int sleepValue = (int) (Math.random() * 1000);
Thread.sleep(sleepValue);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
测试类
public class Test {
public static void main(String[] args) throws Exception {
MyThreadA a = new MyThreadA();
MyThreadB b = new MyThreadB();
a.start();
b.start();
for (int i = 0; i < 10; i++) {
Tools.t1.set("main " + (i + 1));
System.out.println(" main get" + Tools.t1.get());
int sleepValue = (int) (Math.random() * 1000);
Thread.sleep(sleepValue);
}
}
}
测试结果
由上可知,通过ThreadLocal可向每一个线程存储自己的私有数据,虽然三个线程都向t1进行set(),但是都能取出自己set的值