黑马程序员 线程分析(一)

原创 2013年12月05日 23:01:02


----------android培训java培训、java学习型技术博客、期待与您交流! -----------

1,线程引发的数据不安全问题介绍

            就拿火车票售票为分析,假设一个火车站有是个售票窗口,需要卖出10张票,不能卖0号票以及负号票,从10往下递减。

用继承Thread类实现:(注意:ticketnum在类中要静态化,否则就是私有成员了,那就是4个线程一共卖出40张票了)

class saleticket extends Thread{
	//因为采用继承方式执行线程对象,所以共享数据要设置为静态的,这样共享数据属于
	//这个子线程类的,而不是具体的某个子线程对象,各个对象可以对这个共享数据操作
	//否则的话,每个线程都可以卖出十张票了,那么多线程就失去原有的意义了
	private static int ticketnum = 10;
	public void run() {
		while(ticketnum>0){
			System.out.println(this.getName()+"卖出第"+(ticketnum--)+"张票");
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

验证结果:


卖出了0号票,说明线程安全出现了问题。这种出问题的几率很难捕捉到,5到6次才能捕捉到这个现象。下面用Runnable方法来实现,把睡眠动作放在打印之前,这样,问题很容易展现出来,能够卖出-2号票。

用Runnable接口实现:(因为Runnable接口的实例对象只有一个,四个线程公用这个对象,所以ticketnum可以为私有成员了,当线程结束是,对象就会被GC回收,节省了内存资源)

class saleticket1 implements Runnable{ 
	//因为saleticket1对象之创建了一次,但是通过这个saleticket1创建了四个线程
	//相当于四个线程对saleticket1对象进行操作,所以就可以把共享数据设置为私有
	//非静态的,这样生命周期就可以缩短很多,节约资源
	private int ticketnum = 10;
	public void run() {
		while (ticketnum>0) {
			try { 
				Thread.sleep(20); 
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread() + "卖出第" + (ticketnum--)
					+ "张票");
		}
	}
}

验证结果:


2,锁的原理

           线程出现了安全问题,那么用什么办法解决呢,那就是用锁机制,锁的使用注意事项:

 //1,在继承thread类中,锁必须是类的静态成员,否则不起作用,线程只是调用自己的锁,对其他线程没有影响
 //2,在实现Runnable接口中,锁不需要是静态成员,因为线程公用一个runnable对象

代码如下:

class synchronized_method extends Thread{
	private static int ticketnum = 10;
	private static Object mutex = new Object();
	public void run() {
		while(true){
			//同步的范围约定,共享数据的第一次和最后一次的使用范围即可其他不需要同步
			//切忌:不要在while循环上上锁,上锁就只有一个线程卖票了
			//助记方法:火车的卫生间	1,红色(有人)关门,类似上锁
			//				...........
			//			2,绿色(没人)开门,类似解锁
			synchronized (mutex) {
				if(ticketnum <= 0){
					break;
				}
				try { 
					
					Thread.sleep(20); 
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println(this.getName()+"卖出第"+(ticketnum--)+"张票");
			}
			
		}
	}
}

结果验证:


3,同步方式在线程创建方式中的可行性分析(继承Thread类)

a,建立私有对象锁

class synchronized_ticket extends Thread{
	private static int ticketmun = 10;
	private Object obj = new Object();

	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true){
			synchronized(obj){
				if(ticketmun <= 0){
					break;
				}
				try {
					Thread.sleep(10);
				} catch (Exception e) {
					// TODO: handle exception
				}
				System.out.println(this.getName()+"sales:\t"+ticketmun--);
			}
		}
	}
	/*
	 * 结论:这种方法不可行
	 * 原因:object对象是线程私有的,且在初始化时使用new方式生成的
	 * 		这样四个线程就有四个Object对象,在run方法中使用同步代码块,锁使用自己
	 * 		创建的obj对象,这样四个线程都是用自己的obj实例,起不到共享数据同步的作用了
	 * */
	
}

验证结果:


b,建立类静态锁

class synchronized_ticket1 extends Thread{
	private static int ticketmun = 10;
	private static Object obj = new Object();

	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true){
			synchronized(obj){
				if(ticketmun <= 0){
					break;
				}
				try {
					Thread.sleep(10);
				} catch (Exception e) {
					// TODO: handle exception
				}
				System.out.println(this.getName()+"sales:\t"+ticketmun--);
			}
		}
	}
	/*
	 * 结论:这种方法可行
	 * 原因:object对象是静态私有的,且在类初始化时显示方式生成的
	 * 		这样四个线程就共享这个Object对象,在run方法中使用同步代码块,锁变成了共享锁
	 * 		,这样四个线程就需要抢夺锁资源了,方法可行
	 * */
}

验证结果:


c,建立私有对象锁,但是锁是一个引用对象,引用从构造函数传进来的Object对象

class synchronized_ticket2 extends Thread{
	private static int ticketmun = 10;
	private Object obj;
	
	public synchronized_ticket2(Object obj){
		this.obj = obj;
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true){
			synchronized(obj){
				if(ticketmun <= 0){
					break;
				}
				try {
					Thread.sleep(10);
				} catch (Exception e) {
					// TODO: handle exception
				}
				System.out.println(this.getName()+"sales:\t"+ticketmun--);
			}
		}
	}
	/*
	 * 结论:这种方法可行
	 * 原因:object对象是私有的,但是在类实例化时引用了一个对象锁,并且
	 * 		这四个线程的obj同时这引用个Obj对象,在run方法中使用同步代码块,锁变成了共享锁
	 * 		,这样四个线程就需要抢夺锁资源了,方法可行
	 * */
}

验证结果:


d,在thread子类中定义同步函数,并且在run方法中调用,这个方法可行吗?

       假设同步函数是静态的,那么就有可能实现线程同步,下面是代码实现,

class synchronized_ticket3 extends Thread{
	private static int ticketmun = 10;
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true){
			if(ticketmun <= 0){
				break;
			}
			try {
				Thread.sleep(10);
			} catch (Exception e) {
				// TODO: handle exception
			}
			show();
		}
	}
	
	public static synchronized void show(){
		System.out.println(Thread.currentThread().getName()+"sales:\t"+ticketmun--);
	}
	/*
	 * 结论:这种方法不可行
	 * 原因:虽说使用了静态同步函数,但是同步的锁还是线程子类对象本身
	 * */
}

验证结果:

e,如果无法在线程子类中实现同步函数,那使用其他类(静态内部类)是否可行呢?

class synchronized_ticket4 extends Thread{
	private static int ticketmun = 10;
	private static synchronized_ticket4.synclass syn = new synchronized_ticket.synclass();
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true){
			if(ticketmun <= 0){
				break;
			}
			try {
				Thread.sleep(10);
			} catch (Exception e) {
				// TODO: handle exception
			}
			syn.sellTicket(ticketmun--);
		}
	}
	
	static class synclass{
		 public synchronized void sellTicket(int tickNum){  
			 if(tickNum >0){
		        try {  
		           Thread.sleep(10);  
		        }catch (InterruptedException e) {  
		        	e.printStackTrace();  
		        }  
		        System.out.println(Thread.currentThread()+". sale: ticket"+ tickNum--);  
		     }  
		  }  
	}
	
	/*
	 * 结论:这种方法完全可行
	 * 原因: 因为内部类被初始化时只有一个类对象,这样就可以同步了
	 * 补充:内部类也可以,只要是在类初始化是被定义为静态的就可以了,不再赘述
	 * 	private static synchronized_ticket4.synclass syn = new synchronized_ticket4().new synclass();
	 * */
}

验证结果:


f,改进方法:将共享对象封装在一个内部类中,着呀更容易理解,同时对共享数据进行的隐藏,

class synchronized_ticket5 extends Thread{
	private static synchronized_ticket5.synclass syn = new synchronized_ticket5().new synclass();
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true){
			try {
				Thread.sleep(10);
			} catch (Exception e) {
				// TODO: handle exception
			}
			syn.sellTicket();
		}
	}
	
	private class synclass{
		private int ticketmun = 10;
		public synchronized void sellTicket(){  
			 if(ticketmun >0){
		        try {  
		           Thread.sleep(10);  
		        }catch (InterruptedException e) {  
		        	e.printStackTrace();  
		        }  
		        System.out.println(Thread.currentThread()+". sale: ticket"+ticketmun --);  
		     }  
		  }  
	}
	
	/*
	 * 结论:这种方法完全可行
	 * 原因: 因为静态内部类被初始化时只有一个类对象,这样就可以同步了
	 * 补充:内部类也可以,只要是在类初始化是被定义为静态的就可以了,不再赘述,更严谨
	 * 		的做法是把内部类私有化,外部无法直接调用
	 * */
}

验证结果:


补充:实际开发中不需要使用私有内部类,普通的一个java类就可以作为锁,只要保证每个线程引用同一个锁对象即可,就是把私有内部类提取到外面,用线程引用就可以了,不需要太严谨。

----------android培训java培训、java学习型技术博客、期待与您交流! -----------

相关文章推荐

黑马程序员 Java自学总结十一 线程

总结内容来源于黑马毕老师的java基础教学视频 线程的概述、创建 进程:是一个正在执行的程序.        每一个进行执行都有一个执行顺序,该顺序是一个执行路径,或叫一个控制单元. ...

黑马程序员 Java基础线程状态

新建:start() 运行:具备执行资格,同时具备执行权; 冻结:sleep(time),wait()—notify()唤醒;线程释放了执行权,同时释放执行资格; 临时阻塞状态:线程具备cpu的执...

黑马程序员——JAVA 线程

线程     进程是一个正在执行的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。而线程就是一个独立的控制单元,控制着进程的执行。并且一个进程中至少有一个线程。  ...

黑马程序员——07 进程与线程

------- Windows Phone 7手机开发、.Net培训、期待与您交流! ------- ——进程 可以理解为现在正在运行的程序 进程与线程的关系:进程包含线程 设一个进程要完成两个任务 ...

黑马程序员--javaSE--ThreadLocal实现线程范围的共享变量

------- android培训、java培训、期待与您交流! ---------- 在写这篇博客之前,我登了下黑马论坛,想看一下自己提出的string和null的问题有回应没, 却意外地发现自己...

黑马程序员--线程二(安全性)

多线程最怕的就是安全问题 原因:当多条yg

黑马程序员_Java线程

------- android培训、java培训、期待与您交流! ---------- 多线程: 在一个程序内部也可以实现多个任务并发执行,其中每个任务称为线程。 创建线程: 1.继承Th...
  • Mr_love
  • Mr_love
  • 2013年05月22日 19:24
  • 231

黑马程序员-------线程

----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------ 1.线程 与 进程       进程是指一个内存中运行的应用程序,每个进...

黑马程序员—线程概述

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——- 多线程: 线程是依赖着进程存在的进程: 正在运行的程序,是系统进行资源分配和调用的独立单位。 ...

黑马程序员——线程

---------------------- android培训、java培训、期待与您交流! ----------------------   1、什么是进程 进程是指计算机正在运行的程序...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:黑马程序员 线程分析(一)
举报原因:
原因补充:

(最多只允许输入30个字)