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

多线程知识梳理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");
			else
				r.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 chan
class 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方都await
			this.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都await
			System.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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值