多线程的安全问题

举个例子说一下多线程的安全问题

public class ThreadDemo implements Runnable {
	private int num = 0;
	@Override
	public void run() {
		num = num+1;
		//-->thread-0     -->thread-1
		System.out.println(num);
	}
}

public class ThreadTest {

	public static void main(String[] args) {
		ThreadDemo t = new ThreadDemo();
		
		Thread  t1 = new Thread(t);
		Thread  t2 = new Thread(t);
		
		t1.start();
		t2.start();
	}

}

两个线程一起启动,我们想要的结果是thread-0进入run方法,打印输出1,紧接着thread-1进入run方法,打印输出2,但是可能发生的情况是:thread-0进入run方法,对num加1后,num变成1,此时线程thread-0停在此处,执行权让给thread-1,再对num运算,变成2,然后两个线程打印出来的都是2了。

解决方法:采用同步(synchronized)

格式为

synchronized (对象) {
//要同步的代码
		}
public class ThreadDemo implements Runnable {
	private int num = 0;
	Object o = new Object();
	@Override
	public void run() {
		synchronized (o) {
			num = num+1;
			//-->thread-0     -->thread-1
			System.out.println(num);
		}
	
	}
}

参数里面的对象可以是任意一个对象,随便什么对象都行,但是如果要实现同步,这个对象必须是唯一的,这里的对象我们称为锁,当thread-0进入run方法后,会持有锁o,此时如果thread-1进来run方法,会被挡在被synchronized包裹的代码块外,不允许进入,当thread-0执行完代码块里的代码后,会释放锁,此时thread-1就可以拿到锁,执行代码块,两个线程相敬如宾,恩恩爱爱,和睦相处,生活中的例子就是“火车上的厕所”,火车上有一个厕所,小明先进去,把门锁上,在里面边玩手机边蹲坑,小刚捂着肚子过来了,发现里面有人,只好在外面等着,等小明弄完,把门打开,小刚就可以进去了。

这时我们把上面的代码改一下

public class ThreadDemo implements Runnable {
	private int num = 0;
	@Override
	public void run() {
		Object o = new Object();
		synchronized (o) {
			num = num+1;
			//-->thread-0     -->thread-1
			System.out.println(num);
		}
	
	}
}

object挪到了run方法里面,由成员变量变成了局部变量,那么两个线程运行run方法的时候,就会new两次对象,相当于两把锁,此时就不能解决安全问题了,我拿着我的锁进代码块,你拿着你的锁进代码块,我们互不限制,就好像火车上有两个厕所,小明上了一个,小刚跑去上另外一个。所以实现同步安全的条件是:多个线程,一把锁。

同步代码块还有一种写法是

public class ThreadDemo implements Runnable {
	private int num = 0;
	@Override
	public void run() {
		synchronized(this){
			num = num+1;
			//-->thread-0     -->thread-1
			System.out.println(num);
		}
	}
}

就是在synchronized的参数里面加this,那么哪个ThreadDemo对象调用了这个方法,这个this就是指哪个对象,只要保证这个对象是唯一的,就可以实现同步安全。

上面讲的是同步代码块,接下来是同步方法,同步方法其实就是在方法修饰符里加一个synchronized关键字,这里说一下静态同步方法

public class ThreadDemo implements Runnable {
	private static int num = 0;
	@Override
	public  void run() {
		add();
	}
	
	public static synchronized void add(){
		  synchronized(ThreadDemo.class){  
			  num = num+1;
				//-->thread-0     -->thread-1
				System.out.println(num);
		   }  
	}
}

如果这个方法是静态方法,那就不能用synchronize(this)了,因为静态方法在类初始化的时候就会加载,但是synchronize(this)在对象调用的时候才加载,所以参数不能用this,我们一般用当前类的字节码文件对象:xxx.class,每一个类的字节码文件对象都是唯一的,所以用它当锁是可以实现同步的。

接下来说说死锁

首先创建两个对象,也就是两把锁

 class MyLock
 {
     static Object locka=new Object();
     static Object lockb=new Object();
 }

然后进行同步的嵌套

  class ThreadDemo implements Runnable
  {
      private boolean flag;
      
    ThreadDemo(boolean flag){
            flag = flag;
    } public void run() { if(flag) { synchronized(MyLock.locka) { //-->thread-0 synchronized(MyLock.lockb) { } } } else { synchronized(MyLock.lockb) { //-->thread-1 synchronized(MyLock.locka) { } } } } }

接下来是测试类

 public class DeadLockTest
 {
     public static void main(String[] args)
     {
         Thread t1=new Thread(new Test(true));
         Thread t2=new Thread(new Test(false));
         t1.start();
         t2.start();
 
     }
     
 }
thread-0拿到a锁,进入代码块,thread-1拿到b锁,进入代码块,此时thread-0不释放a锁,等着对方释放b锁,对方不释放b锁,等着thread-0释放a锁,相持不下,形成死锁。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值