一、线程通信
1.生产者与消费者案例
分析案例:
①生产者和消费者应该操作共享的资源(实现方式)
②使用一个或多个线程来表示生产者
③使用一个或多个线程来表示消费者
代码实例:
//生产者类
public class Producer implements Runnable {
private ProducerAndConsumer resource = null;
public Producer(ProducerAndConsumer resource) {
this.resource = resource;
}
public void run() {
for (int i = 0; i < 50; i++) {
if (i % 2 == 0) {
resource.push("Mille", "Male");
} else {
resource.push("Felece", "Female");
}
}
}
}
--------------------------------------------------------
//消费者类
public class Consumer implements Runnable {
private ProducerAndConsumer resource = null;
public Consumer(ProducerAndConsumer resource) {
this.resource = resource;
}
@Override
public void run() {
for (int i = 0; i < 50; i++) {
resource.popup();
}
}
}
--------------------------------------------------------
//共享资源对象(姓名 性别)
public class ProducerAndConsumer {
private String name;
private String gender;
private boolean isEmpty = true;//表示共享资源对象是否为空的状态
//push用于生产者向共享资源对象中存储数据
synchronized public void push(String name, String gender) {
//false:
try {
while (!isEmpty) {//当isEmpty为false的时候,不空,等着消费者来消费
this.wait();//使用同步锁对象来调用当前线程释放同步锁,进入等待池中,只能被其他线程唤醒
}
this.name = name;
Thread.sleep(10);
this.gender = gender;
isEmpty = false;//设置数据不为空
this.notify();//唤醒一个消费者
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//消费者从共享资源对象中取出数据
synchronized public void popup() {
try {
while (isEmpty)//true时候,等待着生产者进行生产
{
this.wait();
}
//消费开始
Thread.sleep(10);
System.out.println(this.name + " " + this.gender);
isEmpty = true;
this.notify();//唤醒一个生产者
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
--------------------------------------------------------
//测试类
public class DebugDemo {
public static void main(String[] args) {
ProducerAndConsumer resource = new ProducerAndConsumer();
new Thread(new Producer(resource)).start();
new Thread(new Consumer(resource)).start();
}
}
wait():执行该方法的线程对象释放同步锁,JVM把该线程放到等待池中,等待其他的线程唤醒该线程。
notify():执行该方法的线程唤醒在等待池中等待的任意一个线程,把线程转到锁池中等待。
notifyAll():执行该方法的线程唤醒在等待池中等待的所有的线程,把线程转到锁池中等待.
注意:上述方法只能被同步监听锁对象来调用,否则报错IllegalMonitorStateException。
2.线程的生命周期
线程可以处于以下几种状态:
1.新建状态:使用new创建一个线程对象,仅仅在堆内存中分配空间而已,在调用start方法之前根本没有启动。
2.就绪状态:线程对象调用了start方法之后,等待jvm的调度,此时线程依然没有运行。
3.运行状态:线程对象获得了JVM的调度,如果存在多个CPU,那么允许多个线程并行运行。
4.阻塞状态:正在运行的线程因为某些原因放弃PCU,此时JVM不会给线程分配CPU直到线程重新进入就绪状态。
5.等待状态:此状态只能被其他线程唤醒,此时使用wait方法,使得JVM把当前线程存在对象等待池中。
6.死亡状态。
3.线程的操作
①sleep操作
线程休眠:让执行的线程暂停一段时间,进入计时等待状态。
方法:static void sleep(long millis)
调用sleep后,当前线程放弃CPU,在指定时间段之内,sleep所在线程不会获得执行的机会。
此状态下的线程不会释放同步锁/同步监听器.
该方法更多的用于模拟网络延迟,让多线程并发访问同一个资源的错误效果更明显.
在开发中也会故意使用该方法。
public class Demo{
public static void main(String[] args) throws InterruptedException {
for(int i = 10; i > 0 ; i--) {
System.out.println("倒计时" + i);
Thread.sleep(1000);
}
System.out.println("OK");
}
}
②join操作
联合线程:
线程的join方法表示一个线程等待另一个线程完成后才执行。join方法被调用之后,线程对象处于阻塞状态。
有人也把这种方式称为联合线程,就是说把当前线程和当前线程所在的线程联合成一个线程。
class Join extends Thread {
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("Join" + i);
}
}
}
public class Demo {
public static void main(String[] args) throws InterruptedException {
System.out.println("Begin");
Join joinThread = new Join();
for (int i = 0; i < 50; i++) {
System.out.println("main" + i);
if (i == 10) {
joinThread.start();
}
if (i == 20) {
joinThread.join();//强制执行该线程
}
}
System.out.println("End");
}
}
③后台线程
后台线程:在后台运行的线程,其目的是为其他线程提供服务,也称为“守护线程"。JVM的垃圾回收线程就是典型的后台线程。
特点:若所有的前台线程都死亡,后台线程自动死亡,前台线程没有结束,后台线程是不会结束的。
比如:GC线程为后台线程,主线程为前台线程。
前台线程创建的子线程默认也是前台的,后台同样。
但是也可以设置。
class DemonThread extends Thread {
public void run() {
for (int i = 0; i < 500; i++) {
System.out.println(super.getName() + i);
}
}
}
public class Demo {
public static void main(String[] args) throws InterruptedException {
System.out.println(Thread.currentThread().isDaemon());//false说明主线程是前台线程
for (int i = 0; i < 50; i++) {
System.out.println("main" + i);
if (i == 10) {
DemonThread t = new DemonThread();
t.setDaemon(true);//设置为后台线程
t.start();//
}
}
}
}
前台线程结束之后,后台线程也会随之结束。但是并不会立即结束。
线程优先级:
每个线程都有优先级,优先级的高低只和线程获得执行机会的次数多少有关,并非线程优先级越高的就一定先执行,哪个线程的先运行取决于CPU的调度。
MAX_PRIORITY=10,最高优先级
MIN_PRIORITY=1,最低优先级
NORM_PRIORITY=5,默认优先级
int getPriority() :返回线程的优先级。
void setPriority(int newPriority) : 更改线程的优先级。
每个线程都有默认优先级,主线程默认优先级为5,如果A线程创建了B线程,那么B线程和A线程具有相同优先级.
注意:不同的操作系统支持的线程优先级不同的,建议使用上述三个优先级,不要自定义.