黑马程序员——06多线程的实现方式与安全机制

------- android培训java培训、期待与您交流! ----------


1.多线程的两种实现方式:

  Thread有多个重构造方法,其中有两种最为常用,两种实现方式就是利用的这两个构造函数

Thread()
          分配新的 Thread 对象。
Thread(Runnable target)
          分配新的 Thread 对象。

1.1继承方式

    1.定义一个新类C,并继承Thread类

    2.在C中复写run方法

    3.实例化一个C类对象,然后调用start方法,线程在运行完毕后会自动销毁。

    这里详细介绍下c.run()和c.start()的区别(c是创建好的一个线程): 

    c.run()就是调用复写的run方法,跟普通的调用方法一样

    c.start()是开启线程并执行run()方法。

1.2继承方式

   1.定义一个新类C,并实现Runnable接口,复写run方法

   2.实例化一个C对象p

   3.实例化一个Thread对象,采用的是第二种构造方法,所以要将p作为参数传进去

   4.调用start();

建议使用继承方式,以为Java只支持但继承,但支持多实现

2.线程的5种状态,主要是靠是否持有执行资格和执行权来分辨的

    新建:start() 

    运行:具备执行资格,同时具备执行权; 

    冻结:sleep(time),wait()notify()唤醒;线程释放了执行权,同时释放执行资格; 

    临时阻塞状态:线程具备cpu的执行资格,没有cpu的执行权; 

    消亡:stop() 或者自动销毁

3.“锁”的由来

    没有锁的多线程是不安全的,举个例子:

    4个窗口同时卖票,买票之前要设定好条件:票数大于0,当票数为1时,窗口1判断条件成立后就开始卖票,

    在还未卖出之前窗口2也判断该条件,发现成立,然后俩个窗口都卖票,但是只剩下一张票了,程序就可能

    卖出负数张票。

    问题的原因:

     当多条语句在操作同一个线程共享的数据时,一个线程对多条语句只执行了一部分,另一个线程就将操作权夺走

     并执行语句,导致了共享数据的错误。

     解决方法:

     当一个线程执行多条语句时,在他未执行完时,其他线程不准执行,等于在线程进入一个房间操作时就在门上

     挂把锁,操作完后再把锁打开。

 3.1 挂“锁”的三种方式

       挂锁的关键在于找准共享的数据,要把涉及到共享数据的代码锁起来,有三种方式:

       1.同步代码块:
                           synchronized(obj)
                           {
                                   //obj 表示同步监视器,是同一个同步对象

                                     /**.....
                                    TODO SOMETHING
                                      */
2.同步方法
格式:
在方法上加上synchronized 修饰符即可。 (一般不直接在 run 方法内,而是在它里面调用! )
synchronized 返回值类型 方法名(参数列表)
                  {
                          /**.....
                        TODO SOMETHING
                            */
                  }
同步方法的同步监听器其实的是 this

3.同步锁

 jkd1.5 后的另一种同步机制:
通过显示定义同步锁对象来实现同步,这种机制,同步锁应该使用 Lock 对象充当。
在实现线程安全控制中,通常使用 ReentrantLock(可重入锁)。使用该对象可以显示地加锁和
解锁。
具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,
但功能更强大。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class X   
  2. {  
  3.  //为了保证锁得唯一性,把锁的定义放在共享数据操作代码的外部,共享数据用try包裹  
  4.  private final ReentrantLock lock = new ReentrantLock();  
  5.  //定义需要保证线程安全的方法  
  6.  public void m(){  
  7.  //加锁  
  8.  lock.lock();  
  9.  try{  
  10.      //... method body  
  11.     }  
  12.  finally{  
  13.      //在 finally 释放锁  
  14.         lock.unlock();  
  15.     }  
  16.   }  
  17. }  

3.2死锁

死锁产生的原因是两把锁互相嵌套,两个线程各持自己的锁不放。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class sisuo {  
  2.       
  3.       
  4.     public static void main(String[] args) {  
  5.           
  6.         Object A = new Object ();  
  7.         Object B = new Object ();  
  8.           
  9.         Thread thread1  = new Thread(new sisuos(true));  
  10.         Thread thread2  = new Thread(new sisuos(false));  
  11.           
  12.         thread1.start();  
  13.         thread2.start();  
  14.     }  
  15.   
  16. }  
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <p>public class sisuos implements Runnable  
  2. {</p><p> private boolean flag ;  
  3.    
  4.  sisuos(boolean flag)  
  5.  {  
  6.   this.flag = flag;  
  7.  }  
  8.    
  9.  public void run ()  
  10.  {  
  11.   while(true){  
  12.   if(flag)  
  13.   {  
  14.    synchronized (suo.A) {  
  15.     System.out.println(" 我有A锁");  
  16.     synchronized (suo.B) {  
  17.      System.out.println("我有AB锁");  
  18.     }  
  19.       
  20.    }  
  21.   }  
  22.   else {  
  23.    synchronized (suo.B) {  
  24.     System.out.println(" 我有B锁");  
  25.     synchronized (suo.A) {  
  26.      System.out.println("我有AB锁");  
  27.     }  
  28.    }  
  29.    }  
  30.   }  
  31.  }  
  32. }</p>  
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <p>public class suo {</p><p> public static Object A = new Object ();  
  2.  public static  Object B = new Object ();  
  3. }</p>  

 

4.线程间通讯

线程间通讯其实就是多个线程共同操作同一个共享资源,但是操作动作不同。

也就是多个实现了Runnabl接口不同的类,他们有不同的run方法

那么怎样保证他们是操作的是同一个共享资源呢?

1.构造方法从外部接受同一个资源

2.单例

4.1等待唤醒机制

为了使2个线程轮流操作,需要加入等待唤醒机制,他需要一个标志while(flag )

等待的方法:

                    锁.wait()

唤醒的方法

                   锁.notify()

需要注意的的是wait和notify的对象必须一致

但这样容易导致死锁(全部wait),所以用notifyAll

使用同步锁之后有另一种等待唤醒方法(推荐使用)

它提供了另一种监视器Condition,

Condition 将 Object 监视器方法(waitnotify 和notifyAll)分解成截然不同的对象,以便通过将这些对象与任意Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized方法和语句的使用,Condition 替代了 Object 监视器方法的使用。

final Lock lock = new ReentrantLock();  

final Condition notFull  = lock.newCondition();

final Condition notEmpty = lock.newCondition();

他最大的好处在于可以定义多个监视器。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //多线程的共享资源,要求有生产功能和销售功能,而且生产一个销售一个  
  2. public class Resource {   
  3.     private boolean  isHas = false;  
  4.     private int count = 0;  
  5.           
  6.     ReentrantLock lock = new ReentrantLock();  
  7.     //通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者。  
  8.     Condition con_pro = lock.newCondition();  
  9.     Condition con_sale = lock.newCondition();  
  10.       
  11.     public void product()   
  12.     {  
  13.         lock.lock();  
  14.         try   
  15.         {  
  16.             while(isHas)  
  17.                 try {con_pro.await();} catch(Exception e){}  
  18.             System.out.println(Thread.currentThread().getName()+ "生产了商品"+ (++count));  
  19.             isHas = true;     
  20.             con_sale.signal();  
  21.         }   
  22.         finally  
  23.         {  
  24.             lock.unlock();  
  25.         }  
  26.     }  
  27.       
  28.     public void Sale ()  
  29.     {  
  30.         lock.lock();  
  31.         try   
  32.         {  
  33.             while(!isHas)  
  34.                 try {con_sale.await();} catch(Exception e){}  
  35.             System.out.println(Thread.currentThread().getName()+"出售了商品"+count);  
  36.             isHas = false;    
  37.             con_pro.signal();  
  38.         }   
  39.         finally  
  40.         {  
  41.             lock.unlock();  
  42.         }  
  43.     }  
  44. }  
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //生产线程  
  2. public class Productor implements Runnable  
  3. {  
  4.     private Resource resource ;  
  5.       
  6.     public Productor(Resource resource) {  
  7.         this.resource = resource;  
  8.     }  
  9.     public void run()   
  10.     {  
  11.         while(true)  
  12.         {  
  13.             resource.product();  
  14.         }  
  15.     }}  
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //销售线程  
  2. public class Customer implements Runnable  
  3. {  
  4. private Resource resource ;  
  5.    
  6.  public Customer(Resource resource) {  
  7.   this.resource = resource;  
  8.  }  
  9.  public void run()   
  10.  {  
  11.   while(true)  
  12.   {  
  13.    resource.Sale();  
  14.   }  
  15.  }  
  16. }  
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <p>public class sisuo {  
  2.    
  3.  public static void main(String[] args) {    
  4.      Resource resource = new Resource();</p><p>     //构造两个生产线程,两个销售线程  
  5.      new Thread (new Productor(resource)).start();  
  6.      new Thread (new Productor(resource)).start();  
  7.      new Thread (new Customer(resource)).start();  
  8.      new Thread (new Customer(resource)).start();    
  9.  }  
  10. }</p>     



------- android培训java培训、期待与您交流! ----------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值