Java之多线程(二)

一.多线程的两种实现方式对比

第一种方式:

       1)自定义类MyThread类,继承自Thread类
       2)重写Thread类中的run()方法

       3)创建MyThread类对象,分别去启动线程
注意:

      1)启动线程的时候,为什么不使用run()方法?
         run()不能作为启动线程的方法,因为该方法的调用相当于调用一个普通方法,并不会出现线程的执行的一种随机性!

      2)启动线程用的start()方法,start方法的执行,通过Jvm调用run()方法.
         一个线程不要连续启动,会出现非法线程状态异常!

第二种方式:
      1)自定义一个类;MyRunnable实现Runnable接口

      2)实现接口的run()方法
      3)创建MyRunnable类对象,创建Thread类对象,将MyRunnable类对象作为参数进行传递,分别启动线程.

问题: 既然有了第一种实现方式,为什么要出现第二种实现方式呢?
       第二种方式优于第一种方式的好处:

       1)避免了Java单继承的一种局限性
       2)更符合Java面向对象的一种设计原则:面向接口编程

          将代码的实现和资源对象( MyRunnable)有效的分离开来(数据分离原则!)

二.线程的生命周期

线程从开始创建的时候,一直到线程的执行,最终到线程的终止!

新建线程:此时此刻该线程没有执行资格,没有执行权
线程就绪:线程有执行资格了,但是没有执行权

      在执行线程之前,线程还可能会阻塞
线程执行:线程有执行资格并且有执行权

线程死亡:线程执行完毕,会被垃圾回收线程中的垃圾回收器及时从内存中释放掉.

三.多线程的同步机制

问题引入:
       需求:某电影院出售某些电影的票(复联3,....),有三个窗口同时进行售票(100张票),请您设计一个程序,模拟电影院售票;为了模拟更真实的场景,加入延迟操作(让我们线程睡100毫秒)

public class SellTicket implements Runnable {
	
	//定义100张票
	private int tickets = 100 ;

	@Override
	public void run() {
		
		
		while(true) {
			try {
				//t1睡 t2睡
				Thread.sleep(100); //t2睡
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
			if(tickets>0) {
				//t1,t2,t3 三个线程执行run里面代码
				//为了模拟更真实的场景(网络售票有延迟的),稍作休息
				
				System.out.println(Thread.currentThread().getName()
						+"正在出售第"+(tickets--)+"张票");//0
			}
		}
	}

}

public static void main(String[] args) {
		
		//创建资源类对象(共享资源类/目标对象)
		SellTicket st = new SellTicket() ;
		
		//创建线程类对象
		Thread t1 = new Thread(st, "窗口1") ;
		Thread t2 = new Thread(st ,"窗口2") ;
		Thread t3 = new Thread(st, "窗口3") ;
		
		//启动线程
		t1.start();
		t2.start();
		t3.start();
	}

程序的设计是好的,但是结果有一些问题:
    1)同一张票被卖了多次
              CPU的执行有一个特点(具有原子性操作:最简单最基本的操作)
    2)出现了0或者负票
             (延迟操作+线程的执行随机性)

1.通过刚才的这个程序,有安全问题(同票还是负票)
  
  如何解决多线程的安全问题?
       校验一个多线程程序是否有安全问题的隐患的前提条件:
      1)当前程序是否是多线程环境
      2)是否有共享数据
      3)是否有多条语句对共享数据进行操作

2.解决方案:就是将多条语句对共享数据操作的代码,用一个代码包起来---->同步代码块(理解为门的开和关)
   格式:
     synchronized(锁对象){
       针对多条语句对共享数据操作代码;
     }

  注意:
      锁对象:一定要同一个锁(每个线程只能使用同一把锁)
      锁对象:任何的Java类(引用类型)  

public class SellTicket implements Runnable {
	
	//定义100张票
	private int tickets = 100 ;
	
	private Object obj = new Object() ;

	@Override
	public void run() {
		while(true) {
			synchronized(obj) { //门的开和关
				if(tickets>0) {
					try {
						Thread.sleep(100); 
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()
							+"正在出售第"+(tickets--)+"张票");
				}
			}
			
		}
	}

}


  3. 如果一个方法一进来就是同步代码块,那么可不可以将同步放到方法来进行声明呢? 可以
    同步方法 :里面锁对象是谁?
    (1)非静态的同步方法:  锁对象:this
private synchronized  void sellTicket() {
			if(tickets>0) {
				try {
					Thread.sleep(100); 
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()
						+"正在出售第"+(tickets--)+"张票");
			}
		}

(2)静态的同步方法:和反射有关 ( 静态同步方法的锁对象:类名.class)
private synchronized static  void sellTicket() {
		if(tickets>0) {
			try {
				Thread.sleep(100); 
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()
					+"正在出售第"+(tickets--)+"张票");
		}
	}

4.线程安全的类
StringBuffer sb = new StringBuffer() ;
		Vector<String> v = new Vector<String>() ;
		Hashtable<String, String> hm = new Hashtable<String,String>() ;

Vector<String>它是线程安全的类,还是不习惯使用这个集合,
         使用ArrayList集合:线程不安全的类,通过Collections集合中的synchronizedList()变成线程安全的类

public static <T> List<T> synchronizedList(List<T> list):返回指定列表支持的同步(线程安全的)列表
List<String> array = new ArrayList(); //线程不安全的类
		
		//public static <T> List<T> synchronizedList(List<T> list)
		//返回指定列表支持的同步(线程安全的)列表
		List list = Collections.synchronizedList(array) ; //线程安全的方法


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值