黑马程序员-关于线程同步、死锁的小总结

---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! -----------------

Java对于多线程的安全问题提出的解决方法:同步代码块。
synchronized(对象){ //这里的对象不重要,它只是提供一个标志位,相当于一个锁。可用Object的对象
 需要被同步的代码块(对共享资源的操作)
}
也可以直接同步一个函数,即把synchronized作为修饰符放在函数声明处就可以了。函数加锁后,函数的锁是this对象,所以如果此加锁非静态函数要是和别的同步代码块同步的话,那么要加一样的锁,同步代码块的参数要用this。
同步的前提:1必须要有两个或者以上的线程。
    2必须是多线程使用同一个锁
好处:解决了多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源
如果同步函数被静态修饰后,使用的锁是该类进内存时该类的字节码文件对象,类名.class 该对象的类型是class。
所以如果此加锁静态函数要是和别的同步代码块同步的话,那么要加一样的锁,同步代码块的参数要用 类名.class。
懒汉式单例应用(延迟加载单例):
class Single{
	private static Single s=null;
	private Single(){}
	public static Single getInstance(){
		if(s==null){
		synchronized(Single.class){
			if(s=null)
			s=new Single();
}

}
}
		return s;
}


这里用双重判断来提高效率,免得所有的进程来都要判断是否上锁。


死锁:同步中嵌套同步

package com.itheima;

class Ticket2 implements Runnable{
	Object obj=new Object();
	private String name;
	
	private static int ticket=200;
	boolean flag=true;
	public void run(){
		if(flag){
		while(true){
				synchronized(obj){
					System.out.print("if+"+Thread.currentThread().getName());
					show();
			}
		}
	}
		else{
			while(true){
			System.out.print("else+++"+Thread.currentThread().getName());
			show();
			}
		}
	}
	
	public synchronized void show(){
		synchronized(obj){
		if(ticket>0){
		try{Thread.sleep(10);}catch(Exception e){}
		System.out.println(Thread.currentThread().getName()+"show--------"+ticket--);
		}
		}
	}
}

public class DeadLockTest1 {
	public  static void main(String[]args){
		Ticket2 tic=new Ticket2();
		Thread t1=new Thread(tic);
		Thread t2=new Thread(tic);
		t1.start();
		try{
		Thread.sleep(10);}
		catch(Exception e){}
		tic.flag=false;
		t2.start();
	}
}

这个例子中,t1 ,t2就会产生死锁。当t2刚进入show方法获取show方法的锁(为t2对象本身)且还没有获得obj锁时,cpu执行t1,这时假设t1拿到obj锁且正要执行show方法,那么就会产生死锁了。t1此时需要得到t2对象这个锁,但是这个锁被t2占有,t2此时需要得到obj这个锁,但是obj又被t1占有,所以两者僵持,就产生了死锁。


 wait(),notify(),notifyAll() 都使用在同步中,因为要对持有监视器(锁)的线程操作。所以要使用在同步中,因为只有同步才具有锁。使用方法: 锁.wait  锁.notify() 锁.notifyAll()

为什么这些操作线程的方法要定义在Object类中呢?
因为这些方法在操作同步中线程时i,都必须要标识他们所操作线程持有的锁,只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒。不可以对不同锁中的线程唤醒。也就是说,等待和唤醒必须是同一个锁。而锁可以是任意对象,所以对锁操作的方法要定义在Object类中。
注意:wait()方法是让线程无终止冻结,交出锁和cpu


Lock接口和Condition接口:JDK1.5 中提供了多线程升级解决方案。将同步synchronized替换成现实lock操作。将Object中的wait notify notifyAll 替换成了Condition对象。该对象可以对Lock锁进行获取。
private Lock lock=new ReentrantLock();实现Lock对象。
lock.lock();加锁
lock.unlock();释放锁对象。建议放在finally{}中。
private Condition condition=lock.newCondition();实现Condition对象。
condition.await();//挂起
condition.signal();//唤醒一个线程
condition.signalAll();//唤醒其他所有线程
如果想唤醒多个线程中的某一个或几个线程,可以通过实例化多个Condition对象,为指定线程分配指定的对象即可。
如多个生产者和多个消费者,就可以给生产者实例化一个condition对象,给消费者实例化一个condition对象。程序中用指定对象的signal方法即可。

class Resource{
	private String name;
	private int count=1;
	private boolean flag=false;
	private Lock lock=new ReentrantLock();
	private Condition condition_pro=lock.newCondition();
	private Condition condition_con=lock.newCondition();
	public void set(String name){
		while(flag){
			lock.lock();
			while(flag)
				try {
					condition_pro.await();//挂起自己这个生产者进程
					this.name=name+"---"+count++;
					System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
					flag=true;
					
					condition_con.signal();//唤醒一个消费者进程
				
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			finally{
				lock.unlock();
			}
			
			
			
			
		}
	}
	
	public void out(){
		while(!flag){
			lock.lock();
			try {
				condition_con.await();//挂起自己这个消费者进程
				System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);
				flag=true;
				condition_pro.signalAll();//唤醒一个生产者进程
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			finally{
				lock.unlock();
			}
		}
	}
}

class Consumer implements Runnable{
	private Resource r;
	public  Consumer(Resource r){
		this.r=r;
	}
	public void run(){
		while(true)
			r.out();
	}
}

class Producer implements Runnable{
	private Resource r;
	public Producer(Resource r) {
		this.r=r;
	}
	
	public void run(){
		while(true)
		r.set("+商品+");
	}
}
public class ProducerConsumerDemo {
	public static void main(String[]args){
		Resource res=new Resource();
		Consumer a=new Consumer(res);
		Producer b=new Producer(res);
		Thread t1=new Thread(a);
		Thread t2=new Thread(b);
		Thread t3=new Thread(a);
		Thread t4=new Thread(b);

		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}


 如何停止线程
开启多线程运行,运行代码通常是循环结构,只要控制住循环,就可以让run方法结束,也就是线程结束。
特殊情况:当线程处于冻结状,就不会读取到标记,那么线程就不会结束。就当没有指定方法让冻结的线程恢复到运行状态时,这时需要对冻结进行清除,强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束。Thread类提供了interrupt()方法,即是让此线程从冻结状态恢复到运行状态。
class StopThread implements Runnable{
	private boolean flag=true;
	
	public synchronized void run(){
		while(flag){
		try {
			this.wait();
		} catch (InterruptedException e) {
			System.out.println(Thread.currentThread().getName()+".......Exception");
			ChangeFlag();
		}
		System.out.println(Thread.currentThread().getName()+"...run");
		}
	}
	public void ChangeFlag(){
		this.flag=false;
	}
}

public class StopThreadDemo {
	public static void main(String[]args){
		StopThread st=new StopThread();
		
		Thread t1=new Thread(st);
		Thread t2=new Thread(st);
		
		t1.start();
		t2.start();
		
		int num=0;
		
		while(true){
			if(num++==60){
				t1.interrupt();
				t2.interrupt();
				
				break;
			}
			System.out.println(Thread.currentThread().getName()+"...main..."+num);
		}
		System.out.println("over");
	}
}

  join:当A线程执行到了B线程的join方法时,那么A线程就会等待,等到B线程都执行完,A才会执行。join用来临时加入线程执行。


---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! -----------------
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值