您的位置:首页 > 职场人生

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

2014-06-27 15:18 211 查看
------- 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 返回值类型 方法名(参数列表)

4000
                  {

                          /**.....

                        TODO SOMETHING

                            */

                  }

同步方法的同步监听器其实的是 this

3.同步锁

 jkd1.5 后的另一种同步机制:

通过显示定义同步锁对象来实现同步,这种机制,同步锁应该使用 Lock 对象充当。

在实现线程安全控制中,通常使用 ReentrantLock(可重入锁)。使用该对象可以显示地加锁和

解锁。

具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,

但功能更强大。

[java] view
plaincopy





public class X   

{  

 //为了保证锁得唯一性,把锁的定义放在共享数据操作代码的外部,共享数据用try包裹  

 private final ReentrantLock lock = new ReentrantLock();  

 //定义需要保证线程安全的方法  

 public void m(){  

 //加锁  

 lock.lock();  

 try{  

     //... method body  

    }  

 finally{  

     //在 finally 释放锁  

        lock.unlock();  

    }  

  }  

}  

3.2死锁

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

[java] view
plaincopy





public class sisuo {  

      

      

    public static void main(String[] args) {  

          

        Object A = new Object ();  

        Object B = new Object ();  

          

        Thread thread1  = new Thread(new sisuos(true));  

        Thread thread2  = new Thread(new sisuos(false));  

          

        thread1.start();  

        thread2.start();  

    }  

  

}  

[java] view
plaincopy





<p>public class sisuos implements Runnable  

{</p><p> private boolean flag ;  

   

 sisuos(boolean flag)  

 {  

  this.flag = flag;  

 }  

   

 public void run ()  

 {  

  while(true){  

  if(flag)  

  {  

   synchronized (suo.A) {  

    System.out.println(" 我有A锁");  

    synchronized (suo.B) {  

     System.out.println("我有AB锁");  

    }  

      

   }  

  }  

  else {  

   synchronized (suo.B) {  

    System.out.println(" 我有B锁");  

    synchronized (suo.A) {  

     System.out.println("我有AB锁");  

    }  

   }  

   }  

  }  

 }  

}</p>  

[java] view
plaincopy





<p>public class suo {</p><p> public static Object A = new Object ();  

 public static  Object B = new Object ();  

}</p>  

 

4.线程间通讯

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

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

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

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

2.单例

4.1等待唤醒机制

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

等待的方法:

                    锁.wait()

唤醒的方法

                   锁.notify()

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

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

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

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

Condition
 将 
Object
 监视器方法(
wait
notify
 和
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
plaincopy





//多线程的共享资源,要求有生产功能和销售功能,而且生产一个销售一个  

public class Resource {   

    private boolean  isHas = false;  

    private int count = 0;  

          

    ReentrantLock lock = new ReentrantLock();  

    //通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者。  

    Condition con_pro = lock.newCondition();  

    Condition con_sale = lock.newCondition();  

      

    public void product()   

    {  

        lock.lock();  

        try   

        {  

            while(isHas)  

                try {con_pro.await();} catch(Exception e){}  

            System.out.println(Thread.currentThread().getName()+ "生产了商品"+ (++count));  

            isHas = true;     

            con_sale.signal();  

        }   

        finally  

        {  

            lock.unlock();  

        }  

    }  

      

    public void Sale ()  

    {  

        lock.lock();  

        try   

        {  

            while(!isHas)  

                try {con_sale.await();} catch(Exception e){}  

            System.out.println(Thread.currentThread().getName()+"出售了商品"+count);  

            isHas = false;    

            con_pro.signal();  

        }   

        finally  

        {  

            lock.unlock();  

        }  

    }  

}  

[java] view
plaincopy





//生产线程  

public class Productor implements Runnable  

{  

    private Resource resource ;  

      

    public Productor(Resource resource) {  

        this.resource = resource;  

    }  

    public void run()   

    {  

        while(true)  

        {  

            resource.product();  

        }  

    }}  

[java] view
plaincopy





//销售线程  

public class Customer implements Runnable  

{  

private Resource resource ;  

   

 public Customer(Resource resource) {  

  this.resource = resource;  

 }  

 public void run()   

 {  

  while(true)  

  {  

   resource.Sale();  

  }  

 }  

}  

[java] view
plaincopy





<p>public class sisuo {  

   

 public static void main(String[] args) {    

     Resource resource = new Resource();</p><p>     //构造两个生产线程,两个销售线程  

     new Thread (new Productor(resource)).start();  

     new Thread (new Productor(resource)).start();  

     new Thread (new Customer(resource)).start();  

     new Thread (new Customer(resource)).start();    

 }  

}</p>     

------- android培训java培训、期待与您交流! ----------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息