一 我们知道了一般方法的锁是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进程都无法进行,这就是死锁。