目录
学习多线程基础,这一篇就够啦!(一):https://blog.csdn.net/weixin_43827227/article/details/96606212
第四部分:线程之间的通信
概念:多个线程在处理同一资源,但是任务却不同。(同一个银行,有人存钱,有人取钱)
等待唤醒机制
涉及的方法:
1,wait(): 让线程处于冻结状态,被wait的线程会被存储到线程池中。
2,notify():唤醒线程池中一个线程(任意).
3,notifyAll():唤醒线程池中的所有线程。
为什么这些方法都必须定义在同步中?
因为这些方法是用于操作线程状态的方法。必须要明确到底操作的是哪个锁上的线程。
为什么操作线程的方法wait notify notifyAll定义在了Object类中?
因为这些方法是监视器的方法。监视器其实就是锁:Synchronized(对象即锁)。
锁可以是任意的对象,任意的对象调用的方式一定定义在Object类中。
过程:
例子:
//用等待唤醒注意点: 1, 同步函数 2,wait和notify成对
//wait,notify,notifyAll这些方法都必须定义在同步中
public synchronized void set(String name,String sex)
{
if(flag)
try{this.wait();}catch(InterruptedException e){}
this.name = name;
this.sex = sex;
flag = true;
this.notify();
}
例子:多生产者多消费者问题
if判断标记,只有一次,会导致不该运行的线程运行了。出现了数据错误的情况。
while判断标记,解决了线程获取执行权后,是否要运行!
notify:只能唤醒一个线程,如果本方唤醒了本方(多个set时),没有意义。而且while判断标记+notify会导致死锁(醒来进行while判断时,再次wait了,所有线程都wait,则死锁)。
notifyAll解决了本方线程一定会唤醒对方线程的问题。
(但此方法既唤醒对方,又唤醒本方,在进行while判断,降低了效率。JDK1.5对此进行了优化)
目前结论:多生产多消费时,用while 和 notifyAll进行判断与唤醒
弊端:既唤醒对方,又唤醒本方,在进行while判断,降低了效率
class Resource2 {
private String nameString="烤鸭";
private int count =1;
private boolean flag =false;
// Lock lock=new ReentrantLock();
// Condition condition =lock.newCondition();
public synchronized void set(String name) {
while(flag)
//if判断标记,只有一次,会导致不该运行的线程运行了。出现了数据错误的情况。
//while判断标记,解决了线程获取执行权后,是否要运行!
//notify:只能唤醒一个线程,如果本方唤醒了本方,没有意义。而且while判断标记+notify会导致死锁。
//结论:多生产多消费时,用while 和 notifyAll进行判断与唤醒
//弊端:既唤醒对方,又唤醒本方,在进行while判断,降低了效率
try {
this.wait();
} catch ( InterruptedException e) {
}
count=count+1;
System.out.println(Thread.currentThread().getName()+"。。。。。生产者。。。。。"+this.nameString+count);
flag=true;
notifyAll(); //notifyAll解决了本方线程一定会唤醒对方线程的问题。
}
public synchronized void out() {//与上面的格式进行对比
// lock.lock();
try {
while(!flag)
try {
this.wait();
} catch ( InterruptedException e) {
}
System.out.println(Thread.currentThread().getName()+"。。。。。。。。消费者。。。。。。"+this.nameString+count);
flag=false;
notifyAll();
} finally {
// lock.unlock();
}
}
}
class Producer2 implements Runnable{
private Resource2 r; //传递参数 创建资源对象,然后传递给set out
Producer2(Resource2 r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.set("烤鸭");
}
}
}
class Consumer implements Runnable
{
private Resource2 r; //传递参数 创建资源对象,然后传递给set out
Consumer(Resource2 r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.out();
}
}
}
public class ProducerConsumerDemo {
public static void main(String[] args) {
Resource2 resource2 =new Resource2();
Producer2 producer2=new Producer2(resource2);
Consumer consumer=new Consumer(resource2);
Thread t1=new Thread(producer2);
Thread t2=new Thread(producer2);
Thread t3=new Thread(consumer);
Thread t4=new Thread(consumer);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
---------------------------------------------------------------------------------------------------------------------------------------------
优化: 新特性Lock,Condition
Lock替代synchronized
Condition替代那些监视器方法(object里的wait,notify和notifyAll)
---------------------------------------------------------------------------------------------------------------------------------------------
区别:以前的同步代码块和同步函数仅仅是封装体,自带锁(对锁的操作:获取、释放是隐式的,看不见)。
而现在将锁封装变成了一个对象Lock:提供了lock() 和unlock() 即获取锁和释放锁的两个方法
可以和任意锁进行组合
------------------------------------------------------------------------------
-------------------------------------------------------------------------------
Lock lock=new ReentrantLock();
Condition condition =lock.newCondition();
public synchronized void out() {
while(!flag)
try {
//this.wait();
condition.await ();
} catch ( InterruptedException e) {
}
this.nameString =nameString+count;
System.out.println(Thread.currentThread().getName()+"。消费者。。。。。。"+this.nameString);
flag=false;
//notifyAll();
condition.signalAll();
}
//这样子功能没有变化,与之前一致。
//------------------------------------------------
//下面是优化后
//若通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者。(则可体现优化所在:一个锁可以显加上多组监视器)
Condition producer_con = lock.newCondition();//监听生产者
Condition consumer_con = lock.newCondition();//监听消费者
public void out() {//与上面的格式进行对比
lock.lock();
try {
while(!flag)
try {
consumer_con.await(); //指定沉睡一个消费者的线程
} catch ( InterruptedException e) {
}
System.out.println(Thread.currentThread().getName()+"。消费者。。。。。。"+this.nameString+count);
flag=false;
producer_con.signal();//指定唤醒一个生产者的线程
} finally {
lock.unlock();
}
}
wait 和 sleep 区别?
1,wait可以指定时间也可以不指定。
sleep必须指定时间。
2,在同步中时,对cpu的执行权和锁的处理不同。
wait:释放执行权,释放锁。
sleep:释放执行权,不释放锁。
停止线程
run方法结束。
怎么控制线程的任务结束呢?
任务中都会有循环结构,只要控制住循环就可以结束任务。
控制循环通常就用定义标记来完成。
但是如果线程处于了冻结状态,无法读取标记。如何结束呢?
可以使用interrupt()方法将线程从冻结状态强制恢复到运行 状态中来,让线程具备cpu的执行资格。
但是强制动作会发生了InterruptedException,记得要处理
守护线程
可以理解为后台线程,前台线程结束时,守护线程(后台线程)自动结束。
线程优先级
t2.setPriority(Thread.MAX_PRIORITY);
创建线程默认优先级为5(1到10,优先级越大越有利,但同样需要去抢占CPU资源)