JAVA从菜鸟到进阶(六)——多线程基础二 懒汉式在多线程时的优化及死锁问题

一 我们知道了一般方法的锁是this(调用对象)
而静态方法的锁是什么呢?

package Test;
class Test implements Runnable
{    private  int flag=0;
	Object obj=new Object();
	 private static  int num=100;
	public void run()
	{  
	 	while(true)
		{  
	 		if(flag==0)
	 		{
	 		synchronized(Test.class) {
			if(num>0)
			{
				try {
					Thread.sleep(100);
					System.out.println(Thread.currentThread().getName()+"..run..同步代码块.."+num--);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
	
	        } 
	                      } 
	        }
	 		if(flag==1)
	 		{
	 			method();
	 		}
         }
	 	}
	 	public synchronized static void method()
	 	{
	 		
				if(num>0)
				{
					try {
						Thread.sleep(100);
						System.out.println(Thread.currentThread().getName()+"..静态方法...."+num--);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
		
		        } 
		                      } 
	 	
	public void SetFlag(int flag)
	{
		this.flag=flag;
	}
}
public class Test2 {
   public static void main(String []args)
   {
	  
	   Test a=new Test();
	   Thread t1=new Thread(a);
	   Thread t2=new Thread(a);
	   Thread t3=new Thread(a);
	   t1.start();
	   t2.start();
	  
	   try {
		Thread.sleep(1000);
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	   a.SetFlag(1);
	  
	   t3.start();
	   
   }
}

经过代码验证,当同步方法的锁是类名.class时,多线程问题解决。
所以静态方法的锁是该类对象的字节码文件。(虚拟机在加载时每个类会创建一个字节码文件对象,这个对象每一个类只有一个。当进行类的实例化创建时,都是以此对象为蓝图而创建)。

二前文写了单例模式,下面主要写单例模式在多线程状态下产生的问题。
单例模式主要是想让类只能创建一个实例化对象, 由三个步骤来进行
①在类中创建一个私有化的对象
②私有化构造方法
③在类中写一个静态方法返回该对象的实例

①饿汉式

class SinglePattern {
 
	private static SinglePattern s=new SinglePattern();
    private SinglePattern() {}
    public static SinglePattern GetInstance()
    {
    	return s;
    }
}//饿汗式,类一加载对象就存在

②懒汉式

class SinglePattern2
{
	private static SinglePattern2 ss=null;
	private SinglePattern2() {}
	public static SinglePattern2 GetInstance()
	{
		if(ss==null)
			ss=new SinglePattern2();
		return ss;
	}
}//懒汉式,类加载时没有对象,只有调用了GetInstance方法才有对象

多线程问题的分析:
①饿汉式
在多个线程运行饿汉式时,都只调用到一句使用到共享数据的语句。不符合多线程问题产生的条件(多个线程运行多条有共享数据的代码),在开发中经常使用饿汉式。
②懒汉式
在多个线程运行懒汉式时,有多个线程同时操作有多条处理共享数据的语句。调用线程可能会产生延迟,其余的线程又进来(造成混乱)。
我们怎么处理懒汉式的多线程问题呢?

class SinglePattern2
{
	private static SinglePattern2 ss=null;
	private SinglePattern2() {}
	public static SinglePattern2 GetInstance()
	{
		if(ss==null)
			ss=new SinglePattern2();
		return ss;
	}
}//懒汉式,类加载时没有对象,只有调用了GetInstance方法才有对象.
class Run implements Runnable
{
	  public void  run()
	{
	    System.out.println(Thread.currentThread().getName()+"...."+SinglePattern2.GetInstance());
	}
}
public class SinglePattern
{
	public static void main(String []args)
	{
	   Run r=new Run();
	   Thread t1=new Thread(r);
	   Thread t2=new Thread(r);
	   t1.start();
	   t2.start();
	   
	}
}
Thread-0....SinglePattern2@120fe90
Thread-1....SinglePattern2@310c33f2

可以看出在多线程获取懒汉模式的实例对象时,获取了多个实例对象。我们要运用同步锁来解决。

解决方式一:

class SinglePattern2
{
	private static SinglePattern2 ss=null;
	private SinglePattern2() {}
	public static synchronized SinglePattern2 GetInstance()
	{
		if(ss==null)
			
			ss=new SinglePattern2();
		return ss;
	}
}//懒汉式,类加载时没有对象,只有调用了GetInstance方法才有对象.
class Run implements Runnable
{
	  public  void  run()
	{
	    System.out.println(Thread.currentThread().getName()+"...."+SinglePattern2.GetInstance());
	}
}
public class SinglePattern
{
	public static void main(String []args)
	{
	   Run r=new Run();
	   Thread t1=new Thread(r);
	   Thread t2=new Thread(r);
	   t1.start();
	   t2.start();
	   
	}
}

在getInstance方法上加同步锁,锁对象时Thread.class
当一个线程执行时判断锁,获得锁,判断为空,创建对象。
当其余线程执行时判断锁,获得锁,判断不为空直接返回原对象。

解决方法二:方法一的优化,方法一效率低(原因是后面的线程一直判断锁,获得锁,判断浪费资源而且无意义)
我们可以用同步代码块进行。

class SinglePattern2
{
	private static SinglePattern2 ss=null;
	private SinglePattern2() {}
	public static  SinglePattern2 GetInstance()
	{
		while(true)
		{
			synchronized(Run.class)
			{
		if(ss==null)
			
			ss=new SinglePattern2();
		return ss;
	  }
      	}
	}
}//懒汉式,类加载时没有对象,只有调用了GetInstance方法才有对象.
class Run implements Runnable
{
	  public  void  run()
	{
	    System.out.println(Thread.currentThread().getName()+"...."+SinglePattern2.GetInstance());
	}
}
public class SinglePattern
{
	public static void main(String []args)
	{
	   Run r=new Run();
	   Thread t1=new Thread(r);
	   Thread t2=new Thread(r);
	   t1.start();
	   t2.start();
	   
	}
}
Thread-0....SinglePattern2@13578842
Thread-1....SinglePattern2@13578842

这种优化方法和方法一没有区别,没有解决问题
提升版

class SinglePattern2
{
	private static SinglePattern2 ss=null;
	private SinglePattern2() {}
	public static  SinglePattern2 GetInstance()
	{
		while(true)
		{  
			if(ss==null)
			{
			synchronized(Run.class)
			{
		if(ss==null)
			
			ss=new SinglePattern2();
		   return ss;
	       }
			}
			else 
		return ss;
      	}
    	}
	}
//懒汉式,类加载时没有对象,只有调用了GetInstance方法才有对象.
class Run implements Runnable
{
	  public  void  run()
	{
	    System.out.println(Thread.currentThread().getName()+"...."+SinglePattern2.GetInstance());
	}
}
public class SinglePattern
{
	public static void main(String []args)
	{
	   Run r=new Run();
	   Thread t1=new Thread(r);
	   Thread t2=new Thread(r);
	   t1.start();
	   t2.start();
	   
	}
}

外面多加一层if判断语句,在第一个线程创建对象后,第二个线程不在判断锁,获取锁,而是直接return之前的锁对象,提高了效率。

三 死锁问题
死锁问题大都是在多重锁的嵌套上发生
比如说甲拿了一只筷子,乙拿了另一只筷子,甲要乙给她一只筷子去吃饭,乙要甲给他一只筷子去吃饭
情况① 乐观情况 合作两个人不定时的都拿到两只筷子,不定时的吃饭
②悲观情况 互相都不给筷子,饿死
死锁示例一

package Test;

class Demo implements Runnable
{   private boolean k=true;
    Object obj=new Object();
   public void  SetK(boolean k)
   {
	   this.k=k;
   }
	private int num=100;
   public  void run()
	{    
	   if(k==true)
	   {
	   while(true)
	   {  
	   synchronized(obj)
	   {
		 show();
       }
         }
	   }
	   if(k==false)
	   {
		   show();
	   }
	}
   synchronized void show()
   {
	   while(true)
	   {
		   synchronized(obj)
		   {
		   if(num>0)
	System.out.println(Thread.currentThread().getName()+"run方法的"+num--);
       }
         }
   }
}
public class Test3 {
  
	public static void  main(String []args)
	{
		Demo d=new Demo();
		
		Thread t1=new Thread(d);
		Thread t2=new Thread(d);
		t1.start();
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		d.SetK(false);
		t2.start();
	}
}

死锁实例二

package Test;
 class myLock
 {
	 public static final myLock LOCK1=new myLock();
	 public static final myLock LOCK2=new myLock();
 }
 class Demo2 implements Runnable
 {   
	 
     private boolean flag=true;
     void SetFlad(boolean flag)
     {
    	 this.flag=flag;
     }
	 private int num=100;
	 public void run()
	 {
		 if(flag==true)
		 {
			 
		 
		 synchronized(myLock.LOCK1)
		 {
			 System.out.println(Thread.currentThread().getName()+"true LOCK1。。run"+num--);
		 
		 synchronized(myLock.LOCK2)
		 {
			 System.out.println(Thread.currentThread().getName()+"true LOCK2..run"+num--);
		 }
		}
	   }
		 if(flag==false)
		 {
			 synchronized(myLock.LOCK2)
			 {
				 System.out.println(Thread.currentThread().getName()+"false LOCK2。。run"+num--);
			 
			 synchronized(myLock.LOCK1)
			 {
				 System.out.println(Thread.currentThread().getName()+"false LOCK1..run"+num--);
			 }
		 }
 }
	 }
 }
public class Test4 {
    public static void main(String []args)
  {
	Demo d2=new Demo();
	Thread t1=new Thread();
	Thread t2=new Thread();
    t1.start();
 
    
	try {
		Thread.sleep(100);
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
       d2.SetK(false);
	   t2.start();
  
  }
}

一般有两个锁a,b嵌套调用
例如有一个进程A需要a,b锁才能执行任务。
一个进程B需要b,a锁才能执行任务。
A抢占了a锁
B抢占了b锁
导致A,B进程都无法进行,这就是死锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值