黑马程序员_【总结】_多线程知识梳理2(end)

来源:互联网 发布:单片机校验位计算 编辑:程序博客网 时间:2024/06/05 01:18
多线程知识梳理2


---------- android培训 、java培训、期待与您交流! ---------- 

---------------------------------------------------------------------------------------------------------------------------------------------

1、实际开发 匿名内部类使用很广泛。
2、使用 Lock 替代 synchronized  显示加锁解锁     >>     lock()、unlock()
3、利用 可以有多个 Condition 对象  进行指定signle 和 await
4、Condition中 await   signal     signalAll 替换原来Object中等待唤醒方法。
5、使用LOCK关键代码:
private Lock lock  = new ReentrantLock();
private Condition conAA = lock.newCondition();
private Condition conBB = lock.newCondition();
6、清除冻结状态:interrupt()   使用时,一定要配对更改 标签。
7、守护线程、后台线程setDaemon(boolean) 依附于主线程,
8、临时加入线程 join() 能够获取执行权,被剥夺执行权的线程只有在该线程结束后才会参加抢夺
执行join() 方法前的线程会和当前线程抢夺资源
9、线程组、优先级 setPriority设定优先级 记住MAX、MIN、MOR、NORM
10、暂停 线程 yield()  临时的释放执行权,减缓线程的运行频率,保证每个线程能较为平均的运行
-------
11、同步代码的锁,最好的是该类中 最唯一的对象  r  最具唯一性
12、共享数据 都必须同步  synchronized(r)  2处都是共享数据
13、通过一个 flag标记,来判断,一个线程是否结束, 通过 wait 等待  和 notify 唤醒机制,
14、wait()  notify() notifyAll    必须同一把锁  ,锁可以是任意的对象,而被任意对象调用的方法定义在Object中
15、notify() 适合仅仅2个线程的时候。当大于2个线程的时候,由于唤醒机制,容易出现安全问题。
16、当同时有多个线程,执行相同功能时候,必须写 while 循环判断标记 使用 notifyAll 唤醒机制
-------------------------------------------------------
【1】
多线程中存在的安全问题
//例子1--2个线程。
class names2 {public static void main(String[] args) {Res r = new Res();new Thread(new input(r)).start();new Thread(new output(r)).start();}}class Res{private String name;private String sex;boolean flag;public synchronized void set(String name,String sex){if(flag)try{this.wait();}catch(Exception e){}this.name = name;this.sex = sex;flag = true;notify();}public synchronized void out(){if(!flag)try{this.wait();}catch(Exception e){}System.out.println(name+">>"+sex);flag = false;notify();}}class input implements Runnable{private Res r;input(Res r){this.r = r;}public void run(){int x = 0 ;while(true){if(x == 0)r.set("lili","11111111");elser.set("达到","-----------");x = (x+1)%2;}}}class output implements Runnable{private Res r;private boolean flag;output(Res r){this.r = r;}public void run(){while(true){r.out();}}}
生产和消费  4个线程 使用上诉逻辑 显然是不行的。
class shangping {public static void main(String[] args) {Resource r = new Resource();new Thread(new Producer(r)).start();new Thread(new Producer(r)).start();new Thread(new Consumer(r)).start();new Thread(new Consumer(r)).start();}}class Resource{private String name;private int num=1;private boolean flag;public synchronized void set(String name){while(flag)// 循环致使每一次都会判断try{this.wait();}catch(Exception e){}this.name = name+".."+num++;System.out.println(Thread.currentThread().getName()+">>>"+this.name);this.flag = true;this.notifyAll();//唤醒全部线程,避免只唤醒本方线程}public synchronized void out(){while(!flag)try{this.wait();}catch(Exception e){}System.out.println(Thread.currentThread().getName()+"================="+this.name);this.flag = false;this.notifyAll();}}//sheng chanclass Producer implements Runnable{private Resource r ;Producer(Resource r ){this.r = r ;}public void run(){while(true){r.set("CPU");}}}class Consumer implements Runnable{private Resource r ;Consumer(Resource r ){this.r = r ;}public void run(){while(true){r.out();}}}
例子2是对例子1进行了线程的扩编, 发现 notify局限性,1、只会判断标记一次,如果改为while
又会出现全部卧倒的可能性。
可以看出,多线程的安全问题是一个需求十分严谨却又痛苦万分的。然后来看看更新优化
【2】
【2-1】1.5新特性:
将同步 synchronized 替换成 显示 的 Lock 
将Objcet 中替换成Condition 对象    wait notify  notifyAll >>>>await   signal     signalAll
似乎没有太大的更新,
但是 Lock 中可以有 多个 Condition 对象 
也就是说,在读取标签的时候,可以指定让持有具体锁的那个对象 await  或者 signal
这样一样,代码就明朗了很多,不会出现只唤醒一方的情况。有效的避免了 全部卧倒。


替换后的代码。其实思路是一样了,不同的就是把加锁,释放锁的过程放在了名面上。
class Resource{private String name;private int num=1;private boolean flag;private Lock lock  = new ReentrantLock();private Condition conAA = lock.newCondition();private Condition conBB = lock.newCondition();public void set(String name){lock.lock();try{while(flag)try{conAA.await();}catch(InterruptedException e){}//true>>让A方都awaitthis.name = name+".."+num++;System.out.println(Thread.currentThread().getName()+">>>"+this.name);flag = true;conBB.signalAll();// A方完事了>>>flag = true>>让B方 醒}finally{lock.unlock();}}public void out(){lock.lock();try {while(!flag)try{conBB.await();}catch(InterruptedException e){}// false>> 让B都awaitSystem.out.println(Thread.currentThread().getName()+"================="+this.name);flag = false;conAA.signalAll();// B 方完事了>>>flag = false>>  让A 方醒} finally{lock.unlock();}}}
InterruptedException : 当一个线程在运行中,被中断的异常。


多线程的运行,通常都是循环结构,控制住 循环,就可以让run方法结束,也就结束了线程。


特殊情况:
当线程处于冻结状态,就不会读取标记,那么线程就不会结束


当没有指定的方式让冻结的线程恢复到运行状态,这时需要对冻结状态进行清除
强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束。
【2-2】
Thread  提供方法: interrupt()   清除冻结状态
使用时,1,interrupt清除,2改变标签值。
class stop implements Runnable{private boolean flag = true;public synchronized void run(){while(flag){try {wait();} catch (InterruptedException e) {System.out.println(Thread.currentThread().getName()+"!!!!!!!!!!!!");flag = false;//【2】清除冻结状态后 改变标签属性 达到结束目的。}System.out.println(Thread.currentThread().getName()+">>>>RUN");}}public void changeFlag(){flag = false;}}public class interrupt {public static void main(String[] args) {stop s = new stop();Thread t1 = new Thread(s);Thread t2 = new Thread(s);t1.start();t2.start();int num = 0 ;while(true){if(num++ ==60){//s.changeFlag();t1.interrupt();//【1】 清除 冻结状态t2.interrupt();break;}System.out.println(Thread.currentThread().getName()+"MAIN------------");}System.out.println("over");}}
【2-3】
void setDaemon(boolean)
设置为守护线程或用户线程(后台线程)前台线程一结束,后台线程都结束
必须设定在 start  前。
上面例子部分修改一下
t1.setDaemon(true);//设定为守护线程t2.setDaemon(true);t1.start();t2.start();int num = 0 ;while(true){if(num++ ==60){//t1.interrupt();//【1】 清除 冻结状态break;}System.out.println(Thread.currentThread().getName()+"MAIN------------");}
当没有 设定  守护线程   的时候,会出现等待现象,
而,当设定了后台后, 主线程 一结束,t1  t2  也跟着结束了。

注释掉 try 部分,   观看是否设定 后台线程  结果更为明显。
while(flag){/*try {wait();} catch (InterruptedException e) {System.out.println(Thread.currentThread().getName()+"!!!!!!!!!!!!");flag = false;//【2】清除冻结状态后 改变标签属性 达到结束目的。}*/System.out.println(Thread.currentThread().getName()+">>>>RUN");}
【2-4】
join() 临时加入线程执行。
当进行多线程运算时,不论谁拥有执行权(假设B),当加入 A.join() 
当读到A.join()  表示A线程要执行权,A 要优先执行。
A 线程结束后,才释放执行权,这个时候B 才会参与和他线程再相互抢夺执行权利
也就说说,A.join() 前只有A时, A执行 
 A.join() 前有A和C时, AC相互抢夺 执行权
 最开始拿到执行权利的B 只能等待A  执行完毕后,才会参与权利抢夺。
public class jion {public static void main(String[] args) {jionD j = new jionD();Thread t1 = new Thread(j);Thread t2 = new Thread(j);t1.start();//t2.start();//当 t2在t1.join() 前时候,会和 t1 抢夺执行权。try {t1.join();} catch (InterruptedException e) {}t2.start();for(int i = 0 ;i <70 ; i++){System.out.println(Thread.currentThread().getName()+"-------"+i);}}}class jionD implements Runnable{public void run() {for(int i = 0 ;i< 70 ; i++){System.out.println(Thread.currentThread().getName()+">>"+i);}}}
总之一句话,join() 会剥夺拥有当前执行权的线程,一直到join()  对应的线程结束才会参与抢夺,至于 join()  对应的线程会不会被其他线程抢夺资源另说。
【2-5-1】
setPriority() 设定优先级
通过 Thread.currentThread().toString() 查看 到线程的优先级和线程组
ThreadGroup 线程组对象  据说用的很少
创建线程组,把线程装入该对象,即是一个线程组
优先级 :1-10级 默认优先级 5
MAX_PRIORITY   >>10
MIN_PRIORITY   >>5
NORM_PRIORITY   >>1

根据上面的例子
t2.setPriority(MAX_PRIORITY); 
这个时候t2  线程的优先级最高,在运行越多时越明显,但不代表 ,其他线程抢夺不到资源。

【2-5-2】
yield () 方法
暂停当前正在执行的线程对象,并执行其他的线程。
class jionD implements Runnable{public void run() {for(int i = 0 ;i< 70 ; i++){System.out.println(Thread.currentThread().getName()+">>"+i);Thread.yield();//上列代码 修改部分}}}
这个时候,t1 t2不论谁进来,会稍微减少线程运行频率,并保证每一个线程都有机会平均性的运行。

【3】
老师给出的,实际开发中 常用的 线程方式, 感觉 匿名内部类的使用是非常普遍的。
public class ThreadTest {public static void main(String[] args) {//new Thread(){public void run(){for(int i = 0 ;i < 100 ; i++ ){System.out.println("匿名内部类线程");}}}.start();for(int i = 0 ;i < 100 ; i++ ){System.out.println("主线程部分");}//Runnable r = new Runnable(){public void run(){for(int i = 0 ;i < 100 ; i++ ){System.out.println("不同封装方式达到同时运行的效果");}}};new Thread(r).start();}}//end














---------------------------------------------------------------------------------------------------------------------------------------------
---------- android培训、 java培训、期待与您交流!----------
黑马官网
0 0
原创粉丝点击