zoukankan      html  css  js  c++  java
  • Java Tread多线程(2)多线程安全问题

    作者 :卿笃军

    原文地址:http://blog.csdn.net/qingdujun/article/details/39348093


    本文演示,Tread多线程安全问题,以及几种解决多线程安全方式(线程同步)。

    1)一个线程不安全的Demo

    2)线程同步(synchronized,函数同步,this锁,Class对象锁


    一、小Demo演示引出线程安全问题:

    package thread.runable1.qdj;
    //1.定义类实现Runnable接口
    class RunDemo1 implements Runnable
    {
    	private int x = 5;
    	//2.覆盖Runnable接口中的run方法
    	//将线程代码存放在run中
    	public void run() 
    	{
    		while (true)
    		{
    			if (x > 0)
    			{
    				//加入sleep(),注意:sleep()会抛出异常
    				try {
    					Thread.sleep(10);  //让线程睡眠10ms
    				} catch (Exception e) {
    					e.printStackTrace();
    				}
    				System.out.println("Runnable:"+x);
    				--x;
    			}
    		}
    	}
    }
    public class CRunableDemo1 {
    
    	public static void main(String[] args) {
    		RunDemo1 r = new RunDemo1();
    		//3.通过Thread类建立线程对象,并将Runnable接口的子类对象作为參数
    		Thread t1 = new Thread(r);
    		Thread t2 = new Thread(r);
    		Thread t3 = new Thread(r);
    		Thread t4 = new Thread(r);
    		//4.使用start开启线程
    		t1.start();
    		t2.start();
    		t3.start();
    		t4.start();
    	}
    }

    执行显示结果:


    以上打印出了0,-1,-2等数字。发现没?(线程出问题了吧!

    !!


    问题:出问题的解决办法是什么呢?

    解释:线程1进入,强制睡眠10ms。此时线程2进入,又强制睡眠10ms;线程3进入又强制睡眠10ms。线程4进入再强制睡眠10ms;

    注意,以上4个线程睡眠时都已经进入了if语句,进入的时候x>0还是成立的;

    好了。线程1醒来,開始打印打印5,4,3,2,这时候--x还没运行。线程2就醒来了。抢去了cpu的运行权.....................


    二、线程同步

    问题:对于上面的问题,我们是不是能够採取一个这种措施?当线程1运行run代码段的时候。我们不让其它的线程来运行,直到线程1运行完,其它的线程才干够进入。

    解决方式:好在Java里面本来就有这种函数。将代码段包裹起来。就能够达到上面问题描写叙述的效果。

    函数名:synchronized,须要一个參数。随便传个对象就ok了(详细參数差别分析。请往下拉见附录1)。

    1)一个简单的解决方式:

    package thread.runable1.qdj;
    //1.定义类实现Runnable接口
    class RunDemo1 implements Runnable
    {
    	private int x = 5;
    	Object obj = new Object();
    	//2.覆盖Runnable接口中的run方法
    	//将线程代码存放在run中
    	public void run() 
    	{
    		while (true)
    		{
    			synchronized (obj) 
    			{
    				if (x > 0)
    				{
    					//加入sleep()。注意:sleep()会抛出异常
    					try {
    						Thread.sleep(10);  //让线程睡眠10ms
    					} catch (Exception e) {
    						e.printStackTrace();
    					}
    					System.out.println("Runnable:"+x);
    					--x;
    				}
    			}
    		}
    	}
    }
    public class CRunableDemo1 {
    
    	public static void main(String[] args) {
    		RunDemo1 r = new RunDemo1();
    		//3.通过Thread类建立线程对象,并将Runnable接口的子类对象作为參数
    		Thread t1 = new Thread(r);
    		Thread t2 = new Thread(r);
    		Thread t3 = new Thread(r);
    		Thread t4 = new Thread(r);
    		//4.使用start开启线程
    		t1.start();
    		t2.start();
    		t3.start();
    		t4.start();
    	}
    }
    
    执行结果显示:


    2)换一种更简单的解决方法:函数同步(将synchronized直接加入在函数前面)

    说明:有没有发现,函数就是对多个语句的打包?可是,函数仅仅是打包。而没有像上面的synchronized一样实现同步。可是有一种方法能够达到这个效果。看以下的小Demo就明确了。

    package thread.runable1.qdj;
    //同步函数
    class SynFunc 
    {
    	private int x = 5;
    	public synchronized void aFunc()
    	{
    		while (true)
    		{
    			if (x > 0)
    			{
    				//加入sleep(),注意:sleep()会抛出异常
    				try {
    					Thread.sleep(10);  //让线程睡眠10ms
    				} catch (Exception e) {
    					e.printStackTrace();
    				}
    				System.out.println("Runnable:"+x);
    				--x;
    			}
    		}
    	}
    }
    //1.定义类实现Runnable接口
    class RunDemo1 implements Runnable
    {
    	private SynFunc syn = new SynFunc();
    	//2.覆盖Runnable接口中的run方法
    	//将线程代码存放在run中
    	public void run() 
    	{
    		syn.aFunc();
    	}
    }
    public class CRunableDemo1 {
    
    	public static void main(String[] args) {
    		RunDemo1 r = new RunDemo1();
    		//3.通过Thread类建立线程对象,并将Runnable接口的子类对象作为參数
    		Thread t1 = new Thread(r);
    		Thread t2 = new Thread(r);
    		Thread t3 = new Thread(r);
    		Thread t4 = new Thread(r);
    		//4.使用start开启线程
    		t1.start();
    		t2.start();
    		t3.start();
    		t4.start();
    	}
    }
    
    执行结果显示:(同上)


    附录1:对于函数锁中synchronized中的參数的解释分析(我们先看一下以下这个小Demo)

    package thread.runable1.qdj;
    
    //1.定义类实现Runnable接口
    class RunDemo1 implements Runnable
    {
    	private int x = 100;
    	Object obj = new Object();
    	boolean flag = true;
    	//2.覆盖Runnable接口中的run方法
    	//将线程代码存放在run中
    	public void run() 
    	{
    		if (flag)
    		{
    			while (true)
    			{
    				synchronized(obj)
    				{
    					if (x > 0)
    					{
    						//加入sleep(),注意:sleep()会抛出异常
    						try {
    							Thread.sleep(10);  //让线程睡眠10ms
    						} catch (Exception e) {
    							e.printStackTrace();
    						}
    						System.out.println("Runnable:"+x);
    						--x;
    					}
    				}
    			}
    		}
    		else 
    		{
    			while (true)
    				aFunc();
    		}
    	}
    	public synchronized void aFunc()
    	{
    		while (true)
    		{
    			if (x > 0)
    			{
    				//加入sleep(),注意:sleep()会抛出异常
    				try {
    					Thread.sleep(10);  //让线程睡眠10ms
    				} catch (Exception e) {
    					e.printStackTrace();
    				}
    				System.out.println("Runnable:"+x);
    				--x;
    			}
    		}
    	}
    }
    public class CRunableDemo1 {
    
    	public static void main(String[] args) {
    		RunDemo1 r = new RunDemo1();
    		//3.通过Thread类建立线程对象。并将Runnable接口的子类对象作为參数
    		Thread t1 = new Thread(r);
    		Thread t2 = new Thread(r);
    		//4.使用start开启线程
    		t1.start();
    		//加入sleep()。注意:sleep()会抛出异常
    		try {
    			Thread.sleep(10);  //让线程睡眠10ms
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		r.flag = false;
    		t2.start();
    	}
    }
    
    执行显示结果:



    郁闷吧,又出现线程安全问题了,但是我们分明都加了锁的呀?这是什么原因导致的呢?事实上,问题就出在了。synchronized里面的參数上。

    函数同步锁定的是this,而上面的小Demo里面,一个是函数同步锁(this),另外一个是obj锁,发现没?解决方式:将obj锁改为this锁就没问题了。

    解决后的小Demo:

    package thread.runable1.qdj;
    
    //1.定义类实现Runnable接口
    class RunDemo1 implements Runnable
    {
    	private int x = 100;
    	Object obj = new Object();
    	boolean flag = true;
    	//2.覆盖Runnable接口中的run方法
    	//将线程代码存放在run中
    	public void run() 
    	{
    		if (flag)
    		{
    			while (true)
    			{
    				synchronized(this)
    				{
    					if (x > 0)
    					{
    						//加入sleep(),注意:sleep()会抛出异常
    						try {
    							Thread.sleep(10);  //让线程睡眠10ms
    						} catch (Exception e) {
    							e.printStackTrace();
    						}
    						System.out.println("Runnable:"+x);
    						--x;
    					}
    				}
    			}
    		}
    		else 
    		{
    			while (true)
    				aFunc();
    		}
    	}
    	public synchronized void aFunc()
    	{
    		while (true)
    		{
    			if (x > 0)
    			{
    				//加入sleep(),注意:sleep()会抛出异常
    				try {
    					Thread.sleep(10);  //让线程睡眠10ms
    				} catch (Exception e) {
    					e.printStackTrace();
    				}
    				System.out.println("Runnable:"+x);
    				--x;
    			}
    		}
    	}
    }
    public class CRunableDemo1 {
    
    	public static void main(String[] args) {
    		RunDemo1 r = new RunDemo1();
    		//3.通过Thread类建立线程对象,并将Runnable接口的子类对象作为參数
    		Thread t1 = new Thread(r);
    		Thread t2 = new Thread(r);
    		//4.使用start开启线程
    		t1.start();
    		//加入sleep()。注意:sleep()会抛出异常
    		try {
    			Thread.sleep(10);  //让线程睡眠10ms
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		r.flag = false;
    		t2.start();
    	}
    }
    
    执行显示结果:



    附录2:提出另外一个问题,既然函数同步锁定的是this,那么例如以下的这个函数要怎样解释?静态函数没有this,怎样锁定?

    public static synchronized void aFunc()
    	{
    		while (true)
    		{
    			if (x > 0)
    			{
    				//加入sleep(),注意:sleep()会抛出异常
    				try {
    					Thread.sleep(10);  //让线程睡眠10ms
    				} catch (Exception e) {
    					e.printStackTrace();
    				}
    				System.out.println("Runnable:"+x);
    				--x;
    			}
    		}
    	}
    解决方式:静态函数,是类函数由类调用。

    事实上这里static函数的锁是Class对象。以下注意看例如以下小Demo中synchronized的參数。

    解决后的小Demo:

    package thread.runable1.qdj;
    
    //1.定义类实现Runnable接口
    class RunDemo1 implements Runnable
    {
    	private static int x = 100;
    	boolean flag = true;
    	//2.覆盖Runnable接口中的run方法
    	//将线程代码存放在run中
    	public void run() 
    	{
    		if (flag)
    		{
    			while (true)
    			{
    				synchronized(RunDemo1.class)
    				{
    					if (x > 0)
    					{
    						//加入sleep(),注意:sleep()会抛出异常
    						try {
    							Thread.sleep(10);  //让线程睡眠10ms
    						} catch (Exception e) {
    							e.printStackTrace();
    						}
    						System.out.println("Runnable:"+x);
    						--x;
    					}
    				}
    			}
    		}
    		else 
    		{
    			while (true)
    				aFunc();
    		}
    	}
    	public static synchronized void aFunc()
    	{
    		while (true)
    		{
    			if (x > 0)
    			{
    				//加入sleep(),注意:sleep()会抛出异常
    				try {
    					Thread.sleep(10);  //让线程睡眠10ms
    				} catch (Exception e) {
    					e.printStackTrace();
    				}
    				System.out.println("Runnable:"+x);
    				--x;
    			}
    		}
    	}
    }
    public class CRunableDemo1 {
    
    	public static void main(String[] args) {
    		RunDemo1 r = new RunDemo1();
    		//3.通过Thread类建立线程对象。并将Runnable接口的子类对象作为參数
    		Thread t1 = new Thread(r);
    		Thread t2 = new Thread(r);
    		//4.使用start开启线程
    		t1.start();
    		//加入sleep(),注意:sleep()会抛出异常
    		try {
    			Thread.sleep(10);  //让线程睡眠10ms
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		r.flag = false;
    		t2.start();
    	}
    }
    

    执行显示结果:(同上)


    參考文献:Java视频 毕向东 主讲

    原文地址:http://blog.csdn.net/qingdujun/article/details/39348093


  • 相关阅读:
    hdu 1823 Luck and Love 二维线段树
    UVA 12299 RMQ with Shifts 线段树
    HDU 4578 Transformation 线段树
    FZU 2105 Digits Count 线段树
    UVA 1513 Movie collection 树状数组
    UVA 1292 Strategic game 树形DP
    【ACM】hdu_zs2_1003_Problem C_201308031012
    qsort快速排序
    【ACM】nyoj_7_街区最短路径问题_201308051737
    【ACM】nyoj_540_奇怪的排序_201308050951
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/5116743.html
Copyright © 2011-2022 走看看