在java中,线程在运行的时候,经常会用到各种方法,控制线程的状态。以下,就是对这些方法的操作demo及原理分析
首先,我们要了解,join方法的注释
Waits for this thread to die.
一直等到这个线程死亡。
因此Thread.join()方法可以保证线程执行结果的可见性。
下面举例说明
public class JoinDemo extends Thread {
private static int i = 0;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(()->{
i=1;
});
Thread t2 = new Thread(()->{
i = 2;
});
t1.start();
// t1.join();
t2.start();
Thread.sleep(100);
System.out.println("result: "+i);
}
}
在这样一个简单的代码中。main方法中创建两个线程,一个初始化i=1,一个初始化i=2;
这个main方法运行的时候,打印出来i的值不确定,t1先执行完,i=2;t2先执行完 i=1
但是当我们将t1.join()注释放开后,执行结果就肯定是i=2;
在这个main方法运行的时候,一共会创建三个线程。
main主线程,t1线程,t2线程。主线程调用了t1.join()方法。那他到这句的时候就会wait。一直会wait到t1die。
只有等t1执行完了。主线程才会继续执行,创建t2.
主要流程如下。
所以,当任意一个线程调用了其他线程的Thread.join()方法,就要等待这个线程执行完毕。
如果是Thread.join(long) ,就是等待线程执行完毕或者时间到。才会继续执行。
所以这个join方法就类似与我们生活中的排队买票:
我一直在排队买票,
少一个人我往前进一步,
忽然我看到女神也过来买票,我说:女神,你join到我前面。那我就要一直等待女神买完票。
这个join方法就是给自己添堵,让自己阻塞。处于wait状态
工作流程:
1、挂起线程并修改运行状态
2、用sleep方法中传入的毫秒数设置一个定时器
3、时间结束,定时器触发,线程会被修改到就绪状态重新等待时间片分配
public class SleepDemo extends Thread {
@Override
public void run() {
try {
System.out.println("begin:" + System.currentTimeMillis());
Thread.sleep(1000);
System.out.println("end :" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
SleepDemo sleepDemo = new SleepDemo();
sleepDemo.start();
}
}
以上是一个简单的线程,触发sleep方法。
但是通过end-begin。发现相差时间超过1000毫秒。多出来的时间就是抢占时间片的时间,这个时间的大小应该跟CPU和当前就绪状态线程数有关。
根据sleep(long)的流程,我们可以推断,long为0时,线程的状态挂起后瞬间变为就绪。
所有sleep(0)的功能类似于Thread.yield()
wait方法很明显。就是让一个线程由运行状态,进入到WAITING状态。
让一个线程由运行状态进入到TIME_WAITING状态
Wakes up a single thread that is waiting on this object's monitor
唤醒一个正在等待这个对象监听器的线程
Wakes up all threads that are waiting on this object's monitor
唤醒所有的在等待这个对象监听器的线程
下面是java中一个经典的通过wait()和notifyAll()方法实现的生产者消费者模型。可以加强自己对这几个方法的理解
生产者:
public class Producer implements Runnable {
private Queue bags;
private int size;
public Producer(Queue queue, int size) {
this.bags = queue;
this.size = size;
}
@Override
public void run() {
int i=0;
while (true) {
i++;
synchronized (bags) {
while (size == bags.size()) {
System.out.println("bags已经满了");
//TODO 当队列满了,生产者阻塞
try {
bags.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
bags.add("bag" + i);
System.out.println("生产者生产了一个bag"+i );
} catch (InterruptedException e) {
e.printStackTrace();
}
//唤醒消费者
bags.notifyAll();
}
}
}
}
消费者:
public class Consumer implements Runnable {
private Queue bags;
private int size;
public Consumer(Queue bags, int size) {
this.bags = bags;
this.size = size;
}
@Override
public void run() {
while (true){
synchronized (bags){
while (bags.isEmpty()){
System.out.println("bags已经空了");
try {
bags.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String bag = bags.remove();
System.out.println("消费者消费了一条数据"+ bag);
bags.notifyAll();
}
}
}
}
通过一个main函数来启动生产者和消费者
public class WaitNotifyDemo {
public static void main(String[] args) {
Queue queue = new LinkedList<>();
int size = 10;
Producer producer = new Producer(queue,size);
Consumer consumer = new Consumer(queue,size);
Thread t1 = new Thread(producer);
Thread t2 = new Thread(consumer);
t1.start();
t2.start();
}
}