线程通讯
- wait(): 等待 如果线程执行了wait方法,那么该线程会进入等待的状态,等待状态下的线程必须要被其他线程调用notify方法才能唤醒。
- notify(): 唤醒 唤醒线程池等待线程其中的一个。
- notifyAll() : 唤醒线程池所有等待 线程。
wait与notify方法要注意的事项:
1. wait方法与notify方法是属于Object对象 的。
2. wait方法与notify方法必须要在同步代码块或者是同步函数中才能使用,且必需要由锁对象调用。
class Product{
public String name;
public float price;
public boolean flag = false;//是否消费
public Product() {
}
}
class Producer implements Runnable{
Product p;
public Producer(Product p) {
this.p = p;
}
@Override
public void run() {
int i = 0;
while(true) {
synchronized (p) {
if (p.flag == false) {
if (i % 2 == 0) {
p.name = "苹果";
p.price = 1;
} else {
p.name = "香蕉";
p.price = 2;
}
System.out.println("唤起消费者,我已生产了" + p.name + "价格" + p.price);
p.flag = true;
i++;
p.notifyAll();
} else {
try {
p.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
class Customer implements Runnable{
Product p;
public Customer(Product p) {
this.p = p;
}
@Override
public void run() {
while(true){
synchronized (p) {
if (p.flag == true) {
System.out.println("唤起生产者,我消费了" + p.name + "价格" + p.price);
p.flag = false;
p.notifyAll();
} else {
try {
p.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
public class Test {
public static void main(String[] args) {
Product product = new Product();
Thread t1 = new Thread(new Producer(product));
Thread t2 = new Thread(new Customer(product));
t1.start();
t2.start();
}
}
守护线程
守护线程与普通线程的唯一区别是:当JVM中所有的线程都是守护线程的时候,JVM就可以退出了;如果还有一个或以上的非守护线程则不会退出。(以上是针对正常退出,调用System.exit则必定会退出),所以setDeamon(true)的唯一意义就是告诉JVM不需要等待它退出,让JVM喜欢什么退出就退出吧,不用管它。
class Demo7 extends Thread {
public Demo7(String name){
super(name);
}
@Override
public void run() {
for(int i = 1 ; i<=100 ; i++){
System.out.println("更新包目前下载"+i+"%");
if(i==100){
System.out.println("更新包下载完毕,准备安装..");
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Test {
public static void main(String[] args) {
Demo7 d = new Demo7("后台线程");
d.setDaemon(true);
d.start();
for(int i = 1 ; i<=100 ; i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
让我们看下当我把d.setDaemon(true);代码注释与不注释的区别,不注释该代码的话我们会发现两个线程正常执行。 如果注释该代码的话当主线程结束后jvm则直接会退出。
main开始
更新包目前下载1%
main:1
main:2
main:3
main:4
main:5
main:6
main:7
main:8
main:9
更新包目前下载2%
main:10
main:11
main:12
main:13
main:14
main:15
main:16
main:17
main:18
main:19
更新包目前下载3%
main:20
main:21
main:22
main:23
main:24
main:25
main:26
main:27
main:28
main:29
更新包目前下载4%
main:30
main:31
main:32
main:33
main:34
main:35
main:36
main:37
main:38
main:39
更新包目前下载5%
main:40
main:41
main:42
main:43
main:44
main:45
main:46
main:47
main:48
更新包目前下载6%
main:49
main:50
main:51
main:52
main:53
main:54
main:55
main:56
main:57
main:58
更新包目前下载7%
main:59
main:60
main:61
main:62
main:63
main:64
main:65
main:66
main:67
main:68
更新包目前下载8%
main:69
main:70
main:71
main:72
main:73
main:74
main:75
main:76
main:77
更新包目前下载9%
main:78
main:79
main:80
main:81
main:82
main:83
main:84
main:85
main:86
main:87
更新包目前下载10%
main:88
main:89
main:90
main:91
main:92
main:93
main:94
main:95
main:96
main:97
更新包目前下载11%
main:98
main:99
main:100
main结束
线程的停止
线程停止的方法
一:停止一个正常的线程可以通过变量控制
class MyThread extends Thread{
boolean flag = true;
@Override
public void run() {
int i = 1;
while (flag){
System.out.println("正在执行"+i);
i++;
}
}
}
public class Test {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
for (int i = 1; i<=100; i++){
if (i == 80){
t1.flag = false;
System.out.println("i=80,t1线程停止");
}
}
}
}
二: 停止一个等待状态下的线程可以通过变量与notify方法合作或interrupt方法与变量合作
注意:把线程的等待状态强制清除,被清除状态的线程会接收到一个InterruptedException。
class MyThread extends Thread{
boolean flag = true;
@Override
public synchronized void run() {
int i = 1;
while (flag){
try {
this.wait();
} catch (InterruptedException e) {
System.out.println("接收到了异常了....");
}
System.out.println("正在执行"+i);
i++;
}
}
}
public class Test {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
for(int i = 0 ; i<100 ; i++){
System.out.println(Thread.currentThread().getName()+":"+i);
if(i==80){
t1.flag = false;
//方式一
t1.interrupt();//把线程的等待状态强制清除,被清除状态的线程会接收到一个InterruptedException。所以这里线程可以捕捉到异常
//方式二
synchronized (t1){
t1.notify();
}
}
}
}
}
线程的加入
class Production extends Thread{
@Override
public synchronized void run() {
System.out.println("机器启动。。。。");
System.out.println("机器生产中。。。。");
System.out.println("机器坏了。。。。");
Repair r = new Repair();
r.start();
try {
r.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("机器工作了。。。。");
}
}
class Repair extends Thread{
@Override
public synchronized void run() {
System.out.println("开始修机器。。。。");
System.out.println("修理中。。。。");
System.out.println("机器修好了");
}
}
public class Test {
public static void main(String[] args) {
Production production = new Production();
production.start();
}
}
通过上面的列子我门可以发现当一个线程如果执行join语句,那么就有新的线程加入,执行该语句的线程必须要让步给新加入的线程先完成任务,然后才能继续执行。
那么关于当多个线程同时运行时当有其他线程B加入的话,程序会停止所有线程优先执行B吗?其实不是我们看下官网中对该函数的解释:**它的名字表示当前的线程会一直等待,直到加入的线程结束,当前反被唤醒。**让我们看看下面的列子。
class MyThread extends Thread{
@Override
public synchronized void run() {
for (int i = 1; i<=5 ; i++){
System.out.println(Thread.currentThread().getName()+"执行"+i);
}
}
public MyThread() {
}
public MyThread(String name) {
super(name);
}
}
public class Test {
public static void main(String[] args) {
MyThread t1 = new MyThread("A");
MyThread t2 = new MyThread("B");
MyThread t3 = new MyThread("C");
System.out.println("=========="+Thread.currentThread().getName()+"开始");
System.out.println("==========t1开始==========");
t1.start();
System.out.println("==========t2开始==========");
t2.start();
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("==========t3开始==========");
t3.start();
System.out.println("=========="+Thread.currentThread().getName()+"结束==========");
}
}
==========main开始
t1开始
t2开始
A执行1
A执行2
B执行1
B执行2
B执行3
A执行3
A执行4
A执行5
B执行4
B执行5
t3开始
main结束
C执行1
C执行2
C执行3
C执行4
C执行5
分析:
发现结果无论怎么样t3线程都是最后执行的,多次实验可以看出t1加入后并不会对加入之前的线程t2有影响,反而是对t3有影响只有t1技术后才可以执行t3。因此,可以得出结论,t1.join()方法只会使主线程进入等待池并等待t1线程执行完毕后才会被唤醒,并不影响同一时刻处在运行状态的其他线程。**它的名字表示当前的线程会一直等待,直到加入的线程结束,当前反被唤醒。**结合这个话看的确如此。