线程之间的通信:---打印机
分析:两个线程:输入线程和输出线程
两个任务:输入任务和输出任务
一个数据:要被两个线程共享
代码书写步骤:
1.创建数据类
2.创建任务类
3.创建线程对象并工作
其中在数据类中实现的时候,需要判断线程是否安全,此时需要用到synchronized
给输入任务与输出任务同时加锁,保证两个任务是同步的
需要给两个任务加同一把锁:
这把锁可以使Object.class,但由于Object的使用范围太广,可能造成不必要的错误,所以不建议使用,还可以使用数据类的对象,因为此对象本身就是被两个任务共享的对象,所以充当锁最适合
synchronized在修饰语句的时候,锁可以自己写,在修饰非静态方法时,锁默认的是this,在修饰静态方法时,锁默认的是当前类的字节码文件
使用等待唤醒机制的前提:1.多线程 2.已经用synchronized同步了
使用方法:等待唤醒机制,具体方法notify()/notifyAll(),wait()
wait():让当前的线程变成了等待的状态,放入了一个池子(线程池),失去了抢cpu的能力,等待唤醒(锁想相当于给当前的线程做了一个标记)
notify():将当前的线程从等待状态唤醒,相当于从池子中取出线程。(唤醒的是同一把锁的任意一个线程)
注意点:
wait()是Object类里面的方法,所以可以被任何子类调用
wait、notify等方法必须由锁对象调用
在写wait()方法时,会报错,报错后,鼠标放在错误上面添加try...catch...方法就可以了
前面解决的都是单生产单消费者的问题:
现在解决多生产者多消费者问题:
分析:
生产者有生产任务
消费者有消费任务
需要创建的对象:
多个生产线程、多个消费线程
两个任务:生产任务、消费任务
一个数据:产品
错误分析:
当有两个消费线程的时候,有可能出现生产一次消费一次,或者生产多次消费一次的情况
原因:当线程被重新唤醒之后,没有判断标记,而是执行执行了下面的代码
解决的办法:将if换成while(换完之后再进程序就需要重新判断)
继续:继续运行程序会出现死锁的情况(4个线程同时处于等待状态)
原因:唤醒的是本方的线程,导致所有的线程都处于等待的状态
解决:将notify换成notifyAll:保证对方线程的唤醒
死锁:
1.所有的线程都处于等待状态
2.锁之间进行了嵌套作用
jdk1.5之后新增了lock和signal来替代了synchronized
lock()和unlock()是Lock类里面的方法
lock()相当于synchronized的左括号
lock()相当于synchronized的右括号
这两个方法必须实现,所以要用到try{}finally{}方法
而await和signal()是Condition类里面的方法
其中Lock类里面有个newCondition方法可以直接创建Condition的对象
实例:
多生产者多消费者
分析:
线程:多个生产者和多个消费者
任务:生产和消费
数据:产品
现列举数据类中生产者的活的代码
class Product{
String name;
int price;
int count;
boolean flag = false;
private final Lock lock=new ReentrantLock()://Lock是个借口
Condition condition1 = lock.newCondition();
Condition cindition2= lock.newCondition();
public void produce(String name,int price){
try{
lock.lock();
while(flag==true){
try{
condition1.await
}catch(InterruptedException e){
e.printStackTrace();
}
this.name = name;
this.price = price;
System.out.println(Thread.currentThread().getName()+" 生产了:"+name+" 产品的数量:"+count+" 价格:"+price);
count++;
flag = !flag;
condition2.signal();
}
}finally{
lock.unlock();
}
}
}
线程的停止:控制任务区的结束,只要任务没有结束,线程就不会停止
1.通过一个标识符停止线程
2.调用stop方法--已经过时,有固有的安全性,不建议使用
3.调用interrupt方法让长期处理wait状态的线程停止
守护线程:相当于后台线程(操作不到的线程,看不见),依赖于前台线程(正在运行,可以看到的,例:主线程),正常情况下,当前台线程结束的时候,不管守护线程是不是还在工作,都会立刻结束
典型的守护线程:垃圾回收线程
当一个线程调用了setDaemon()方法,并将参数设置成true,这个线程就变成了守护线程
注意:这个方法一定要在start()方法之前调用
形式:Thread thread = new Thread(new Test1())
thread.setDaemon(true);
thread.start();
此时,thread线程就是守护线程,随着前台线程的结束而结束
join()方法:
原理:线程一旦调用join方法,他的优先级就会高于主线程,主线程会等当前的线程执行完之后再去执行
注意:这个线程的优先级只是比主线程的高,对其他线程没有影响;且join加到start()后面,线程开启后,再调用此方法
作用:当我们希望当前线程对应的任务在主线程之前完成
Thread thread1 = new Thread(new Test());
thread1.start();
thread1.join();
此时thread1的优先级就比主线程的高,会优先于主线程执行
网友评论