学习目标:
掌握多线程–》jdk1.5后的新特性
1.将锁封装成lock对象(隐式变成显示)
2.新的监视器对象 Codition 中的 await()方法 signal()方法代替了 Obj 中的方法 wait() notify方法
学习内容:
①什么是锁(lock)对象 监视器(Condition)对象
jdk 1.5版本后出现的代替同步锁的方法和代替Object中的wait notify方法
②为什么要用锁(lock)对象 监视器(Condition)对象
1.替代升级的锁对象和监视器对象,更为简洁方便,还能提高效率。使用起来也是一目了然
2.把原来繁琐的加锁对象还要找到对应的锁进行同步才能保证多线程之间的安全性
现在可以直接进行锁对象的提取与加锁。
3.监视器对象中的唤醒(signal())方法 可以直接进行指定的线程唤醒,从原来的全部唤醒到现在指定对象的唤醒,提高了线程的效率。
③怎么使用锁(lock)对象 监视器(Condition)对象
lock lock = new ReentrantLock();创建lock锁对象,后面需要带一个fianlly{ }代码块进行 关闭锁的动作
如:
public void show()
{
lock.lock();//开启锁
//插入需要同步的代码块
fianlly
{
lock.unlock();//关闭锁
}
}
创建一个监视器对象
Condiction pro_lock = lock.newCondition(); //锁对象的监视器方法 保证了监视器的唯一性
pro_lock.await();//监视器中的等待方法(指定)
pro_lock.signal();//监视器中的唤醒方法(指定)
—wait和sleep的区别:
1.wait 可以指定时间也可以不指定 sleep必须指定时间
2.在同步中,对cpu的执行权和锁的处理不同
wait:对cpu 释放执行权,释放锁
sleep:对cpu释放执行权,不释放锁
图解:
学习产出:
3篇代码:(jdk1.5的新特性 监视器方法)
/*jdk 1.5以后的版本 lock() nulock();释放锁;通常需要定义finall代码块
*
* Condition接口:出现替代了object中的方法wait() notify() notifyAll
* 将监视器进行单独的封装。变成Codition监视器对象
* Condition:await() 睡眠方法 signal();
*
*
* */
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadDem$2 {
public static void main(String[] args) {
//创建资源
Resourcesp r =new Resourcesp();
//创建任务
Productionp p = new Productionp(r);
Consumptionp c = new Consumptionp(r);
// 创建线程 开启任务
Thread t0 = new Thread(p);
Thread t1 = new Thread(c);
Thread t2 = new Thread(p);
Thread t3 = new Thread(c);
t0.start();
t1.start();
t2.start();
t3.start();
}
}
class Resourcesp
{
private String name;
private int count = 1;
boolean flag = false;
//新建锁对象
Lock lock = new ReentrantLock();
//新建监视器对象 生产者 代替wait notify方法
Condition pro_lock = lock.newCondition();
//新建监视器对象 消费者者 代替wait notify方法
Condition con_lock = lock.newCondition();
//对外提供制作方法(生产烤鸭)
//使用新的锁对象 lock
public void set(String name)//由于有了新的锁对象 就不需要同步函数了
{
lock.lock();//开启锁 隐式变显示
try {
//需要同步的代码 生产烤鸭
//判断真假值(是否需要生产)由于任务中有多个线程执行 需要使用while循环判断标记(flag)
while(flag)
{
//等待判断的结果返回,使用的是新版监视器对象的await()方法
try {pro_lock.await();}catch(InterruptedException e){}
}
//生产烤鸭 打印出来
this.name = name+count;//把count也封装到堆内存的 name中 (堆内存 共享数据 生命周期长)
System.out.println("生产:"+this.name +":"+Thread.currentThread().getName());
//每次打印后都加1
count++;
//1.改判断标记然后2.唤醒别的线程之后3.关闭锁
this.flag = true;
//唤醒消费线程
con_lock.signal();
} finally {
//关闭锁
lock.unlock();
}
}
public void output()
{
lock.lock();
//加入需要同步的代码块
try {
//加入需要执行的代码 先判断标记 是否需要执行
while (!flag)
{
try {con_lock.await();}catch(InterruptedException e) {}
}
//消费烤鸭 打印出来
System.out.println("消费:" + this.name+":"+Thread.currentThread().getName());//从堆内存提取数据出来 (共享数据)
// 改标记(已经执行完毕) 唤醒生产者进行消费
this.flag = false;
pro_lock.signal();
} finally {
//关闭锁
}
}
}
//生产者
class Productionp implements Runnable
{
Resourcesp r;
//创建本类构造函数传入资源对象r 拿到的资源(Resources)对象的唯一性
public Productionp(Resourcesp r) {
this.r = r;//保证唯一性
}
public void run() {
while (true) {
r.set("烤鸭");
}
}
}
//消费者
class Consumptionp implements Runnable
{
Resourcesp r;
//创建本类构造函数传入资源对象r 拿到的资源(Resources)对象的唯一性
public Consumptionp(Resourcesp r) {
this.r = r;//保证唯一性
}
public void run() {
while(true)
{
r.output();
}
}
}
同步外的线程可以活多个,但是要进到同步中只有拿到锁的那个线程才可以执行
class Demo
{
void show() t0 t1 t2
{
synchronized()
{
wait();
}
}
void method()
{
t3
synchronized()
{
notify();
}
}
}
图解
图解二
学习计划的总量
1、 技术笔记 1 遍
2、CSDN 技术博客 1 篇
3、 学习的 vlog 视频5个(jdk1.5的新特性 监视器方法系列视频)(多生产多消费的while notifyAll问题的解决)(31-jdk1.5解决办法–范例)(32-wait和sleep的区别)
随手笔记:
生产消费需要循环一遍 才会出错
(第一遍线程进入4线程各自抢执行权,由于生产消费都有wait方法所以必然会放弃执行权,放弃锁,但是线程进入后唤醒本类会直接挂在在线程池中,生产消费全都挂在之后会发生死锁)
判断标记用while 比较安全
Thread0 和Thread1同时运行
不用while判断标记会导致多生产多消费的情况出现:if只判断一次(会导致下次被唤醒不判断标记直接进行生产) while判断多次就没有这个问题,为false就等待
不用notifyAll会导致死锁 原因:notify会随机唤醒一个线程,假如继续唤醒本类线程会导致线程一直冻结状态,当全部线程冻结之后就会产生死锁
lock 锁对象将synchronized(同步封装体)
换锁之后不能使用this.wait了 不是同步函数中的锁了
java.util.concurrent 软件工具包
同步代码块对于锁的操作是隐式的–》释放锁的动作是隐式的、
构造方法可以进行new对象
ReenttrantLock():一个可重入的互斥的锁(lock开启 nulock解锁)
Lock lock = new ReenttrantLock();返回一个lock对象
同一个锁内可以挂载多个Condition的监视器对象 如一个锁对应有一个生产锁 一个消费锁 可以进行任意的组合(由此想到了指定的唤醒动作)
注意:nulock需要定义try{}finally{}块中 以免前面的线程出错后 拿着锁一直不释放**(运行完毕后需要进行释放)**
易错 :(fianlly) 正确写法: (finally)
interface Condition
{
await();
signal();
singnalAll();
}
pro.newCondition();根据锁获取监视器对象
con.newCondition();
Condition: await signal signalAll
同步外的线程可以活多个,但是要进到同步中只有拿到锁的那个线程才可以执行
class Demo
{
void show() t0 t1 t2
{
synchronized()
{
wait();
}
}
void method()
{
t3
synchronized()
{
notify();
}
}
}
9.5思考
public void set(String name)//由于有了新的锁对象 就不需要构造函数了
{
lock.lock();//开启锁 隐式变显示
try {
//需要同步的代码 生产烤鸭
//判断真假值(是否需要生产)由于任务中有多个线程执行 需要使用while循环判断标记(flag)
while(flag)
{
//等待判断的结果返回,使用的是新版监视器对象的await()方法
try {pro_lock.await();}catch(InterruptedException e){}
}
//生产烤鸭 打印出来
this.name = name+count;//把count也封装到堆内存的 name中 (堆内存 共享数据 生命周期长)
System.out.println("生产:"+this.name +":"+Thread.currentThread().getName());
//每次打印后都加1
count++;
//1.改判断标记然后2.唤醒别的线程之后3.关闭锁
this.flag = true;
//唤醒消费线程
con_lock.signal();
} finally {
//关闭锁
lock.unlock();
}
}
public void output()
{
lock.lock();
//加入需要同步的代码块
try {
//加入需要执行的代码 先判断标记 是否需要执行
while (!flag)
{
try {con_lock.await();}catch(InterruptedException e) {}
}
//消费烤鸭 打印出来
System.out.println("消费:" + this.name+":"+Thread.currentThread().getName());//从堆内存提取数据出来 (共享数据)
// 改标记(已经执行完毕) 唤醒生产者进行消费
this.flag = false;
pro_lock.signal();
} finally {
//关闭锁
}
}
}
//生产者
class Productionp implements Runnable
{
Resourcesp r;
//创建本类构造函数传入资源对象r 拿到的资源(Resources)对象的唯一性
public Productionp(Resourcesp r) {
this.r = r;//保证唯一性
}
public void run() {
while (true) {
r.set("烤鸭");
}
}
}
9.5今日难点:
class Resources
{
//创建锁对象 与监视器对象
Lock lock = new ReentrantLock();
//两监视器对象 生产与消费
Condition pro = lock.newCondition();
Condition con = lock.newCondition();
//创建一个boolean 型变量 判断单位是否需要存或者取
private boolean flag =false;
//创建一个数组 存一百个单位
final Object[] arr = new Object[100];
//创建一个名字
private String name;
//创建一个生产线程
//创建三个变量 count记录变化值 save和tack记录的是生产了多少只或者取了多少只 (随着count而进行变化)
int save,tack,count;
public void set(Object x)
{
//加锁同步
lock.lock();
//加入try finally块
try
{
//生产烤鸭一百只 如果满一百只就唤醒消费者进行消费
//把生产的一百只烤鸭装进arr[]数组
while (count == arr.length) {
try {pro.await();}catch(InterruptedException e) {}
//开始装箱
//需要生产多少就存进数组
arr[save] = x;
if (++save == arr.length) save = 0;{
count++;
}
con.signal();
}
}
finally
{
lock.unlock();
}
}
public Object tack()
{
lock.lock();
try {
while (count == 0) {
try {con.await();}catch(InterruptedException e) {}
}
Object x = arr[tack];
if (++tack == arr.length) tack = 0; {
--count;
}
pro.signal();
return x;
}
finally
{
lock.unlock();
}
}