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


  • 相关阅读:
    algorithm
    jstl
    jsp
    cookie
    变量和方法调用过程中会出现的参数传递
    http请求
    weblogic 的安全域问题
    web service
    行业充电
    客户端生成web service
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/5116743.html
Copyright © 2011-2022 走看看