黑马程序员-------java基础之线程

来源:互联网 发布:java开源用户管理系统 编辑:程序博客网 时间:2024/05/29 16:08

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

一 线程是啥?

     每一个运行着得程序对应一个进程,是cup分配资源的最小单元。

     而每一个进程都包含一个和多个线程,程序(进程)所完成的功能,实际是由一个个独立的线程进行完成的。

    比如:

   JVM启动的时候会有一个进程:java.exe该进程中至少有一个线程在负责java程序的执行,而且这个
线程运行的代码在于main方法中。该线程成为主线程。

二java中创建线程的方式

   方式1:继承实现方式

    1  定义一个Thread类的子类

    2  复写run() 方法

         目的:定义该线程所需要执行的代码。即定义该线程的功能

    3 创建该类对象

   4  该对象.start()

       作用:让创建的线程处于可抢夺CUP的状态(临时状态或就绪状态)

  方式2:实现方式

   1  定义一个类实现Runnable接口

   2  复写run()方法

   3 用该类对象作为参数创建一个Thread对象:  Thread  t = new Thread(实现Runnable接口类的对象)

  4 t.start();//让创建的线程处于可争夺CPU的状态

为啥2种方式都要复写run方法?

因为JAVA中定义Thread类来描述线程

 1   该类定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。
   也就是说:thread类中的run方法,用于存储线程要运行的代码。

 2  Thread类也实现了Runnable接口

实现方式和继承方式的区别?

实现方式的好处:避免了单继承的局限性,
                                在定义线程时,建议使用实现方式。

两种方式的区别:
   继承Thread类:线程代码存放在Tread子类的run()方法中。
   实现Runnabel接口:线程代码在接口子类的run()方法中。


线程的几种状态:



三 线程安全问题

   为啥会出现安全问题?

    原因:不同线程对线程共享数据进行访问的,多条代码可能只执行一部分之后就停止(CPU的切换)

  java 为线程线程安全问题所提供的专业解决办法:

 同步代码块:

  synchronized(对象)

    {

          操作共享数据的代码

     }

  对象相当于锁,持有锁的线程可以再同步代码块中执行,没有锁的线程即使获得了cpu
  的执行权,也进不去。保证了对共享数据操作的安全性。

 保证多个线程同步的前提:

   1   必须是2个或2个以的线程

   2    必须保证是同一个锁。

           目的: 保证了同步代码块儿中的代码只有一个线程在运行。
 

同步的缺点:多个线程需要进行锁的判断,较为消耗资源。

如何确定同步代码块儿所加的位置:

1 明确哪些代码是多线程运行的代码。-----Run()中的所有

2 明确共享数据。

3 明确多线程运行代码中的哪些语句是操作共享数据的。//同步代码块的使用地方

-------------------------------------------------------------

同步的第二种实现方式:同步函数

例如:

      public synchronized void  add(int n)
{

}

同步函数用的是哪一个锁?
函数需要被对象调用,那么函数都有一个所属的引用对象。就使用的锁是this。
所以函数是this

静态函数的同步锁是该方法所在类的字节码文件对象类名.Class;

因为静态函数不存在this引用


死锁:

死锁发生的原因:1 存在多个锁

                                 2 同步中嵌套者同步

示例:

class Test implements Runnable{@Overridepublic void run() {// TODO Auto-generated method stubsynchronized(Lock.locka){    System.out.println("Test locka");synchronized(Lock.lockb){System.out.println("Test lockb");}}}}class Test2 implements Runnable{@Overridepublic void run() {// TODO Auto-generated method stubsynchronized(Lock.lockb){System.out.println("Lockb  test2");synchronized(Lock.locka){<pre name="code" class="java">                         System.out.println("locka test2");
}}}}class Lock{static Object locka = new Object();static Object lockb = new Object();}public class deadTreadDemo {public static void main(String[] args) {// TODO Auto-generated method stubTest t = new Test();Test2 t2 = new Test2();Thread th1 = new Thread(t);Thread th2 = new Thread(t2);th1.start();th2.start();}}

当 th1 线程获得cpu后执行到
System.out.println("Test locka");失去了CPU的执行权, 但并未释放同步锁对象locka
此时th2 线程获得cpu的执行全当执行到:

System.out.println("Lockb  test2");失去了cpu的执行权,但并未释放同步锁对象lockb
此时th1再次获得cpu执行权,但并未获得lockb对象因而无法进入下一个同步块,无法执行
System.out.println("Test lockb");而无法结束线程释放同步锁locka
当一段时间后Th2线程在获得cpu执行权,但并未获得同步锁对象locka因为无法执行下一个同步块

无法执行

<pre name="code" class="java"> System.out.println("locka test2");

无法结束线程释放同步锁lockb

这样就导致2个线程再循环等待资源,死锁。

------------------------------------------------------------------------------------------

线程间的通信:

wait();

notify();

notifyAll();

以上方法都定义在Object类中;因为锁是任意对象,而线程的wait(),notify();notifAll()只是针对某个具体的锁而言的。实质是: 锁对象.wait();锁对象.notify();


通信:一个线程唤醒另一个线程

问题描述:当存在多个线程对某一共享资源进行操作时,线程之间的操作应当满足一定的顺序时(如:先输入才能输出)如何实现?

此时就要用到线程之间的通信机制。

实例:

生产者消费者问题

<pre name="code" class="java">// class Resource  {     private boolean flag = false;//信号量:标志是否有商品     private String name;     private int num =0;      public void put(String name)throws InterruptedException//name 和num是共享数据       {         synchronized(this)//只是保证同步了 但还并未保证先生产后消费:不能保证生产一个消费一个交替运行         {          if(flag)//如果有商品 则生产停止           {              wait();//放弃CPU的执行权等待消费者的唤醒            }             this.name = name;             num++;             System.out.println(name+"----生产者-----"+num);             flage = true;//有货了             notify();//打电话给消费者说有货了         }       }     public void get()throws InterruptedException      {         synchronized(this)        {           if(!flag)//没有消费品             {                wait();//消费者等待             }             System.out.println(name+"----消费者-------"+num);             flag = false;//没货了             notify();//通知生产者没货了(唤醒生产线程)        }      }      } class Producer implements Runnable{  private Resource r;  public Producer(Resource r)   {            this.r = r;   }   public void run()     {       while(true)        {          try          {            r.put("商品");           }           catch(InterruptedException e)           {           }        }      }} class Consumer implements Runnable{  <pre name="code" class="java"><pre name="code" class="java"> private Resource r;  public Consumer (Resource r)   {       this.r = r;   }   public void run()     {       while(true)        {           try           { r.get();           }           catch(InterruptedException e )            {            }        }      }


}

class ProConDemo1
{
public static void main(String[]arg)
{
Resource r = new Resource();
Producer pro = new Producer (r);
Consumer con = new Consumer(r);

Thread t1 = new Thread(pro);
Thread t2 = new Thread(con);
t1.start();
t2.start();
}
}
新问题的产生:

 当存在多个消费者线程和多个生产者线程时:

 可能会出现:生产多个商品但值被消费一个;或者是生产一个商品被多个消费者消费的情况

 产生原因:

       1   当线程在if(){}块中wait()之后,当再次获得CPU执行权时不会再对flag进行判断;从而出现多次消费,或者多次生产的现象;

             解决办法:将if(flag)改为:while(flag);  让线程始终对flag进行判断

     2   改成while()之后出现新的问题:

        所有线程都wait()了。让程序无法正常执行。

        原因:notify();唤醒的是:先进入等待所队列中的线程,这样有可能唤醒的是己方(同为生产者线程或者同为消费者线程)线程;

        解决办法:notifyAll();

具体分析:

<pre name="code" class="java"><img src="http://img.blog.csdn.net/20140822103745437?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMjE5MDQ5OQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" height="710" width="907" alt="" />


JDK1.5中提供新方法解决锁问题:

用实现Lock接口的类 代替synchronized,使用condtion 来进行wait(),notify() 等操作

lock的特点:可以为同一个lock设立多个condition对象,为不同线程分类:如将线程分为生产者线程类,消费者线程类。当线程间进行唤醒操作时可以对同一个lock的不同类线程进行唤醒。

实例:

<pre name="code" class="java"> import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.ReentrantLock;/*JDK 1.5以及之后对锁的处理 *  1 用 Lock接口的实现类对象 替代 synchronized *  2 用 condition 来进行wait(),notify(),notifyAll()操作 *  3 特点: *     1 需显示的加锁  Lock  lock.lock(); *     2 显示释放锁    lock.unlock(); *     3 一个锁可对应多个condition对象 * */class Product1//拉煤优化进程间通信 优化代码{    private String name;    private int num = 0;    private boolean flag = false;    private ReentrantLock lock  = new ReentrantLock();    private Condition producer_con;    private Condition consumer_con ;    public Product1(String name)    {        this.name = name;        producer_con = lock.newCondition();        consumer_con = lock.newCondition();    }            //生产行为    public void put()throws InterruptedException    {                       lock.lock();//显示调用加锁               try               {                    while(this.flag)                    {                       producer_con.await();//可能出现异常,出现异常时必须释放锁                           }                      System.out.println(name+"---"+"生产者----"+(++num));//模拟生产者进行的操作处理                      flag = true;                    //this.notifyAll();//唤醒消费者                    consumer_con.signal();               }finally               {                lock.unlock();//显示释放锁                 }    }    //消费行为    public  void get() throws InterruptedException    {                      lock.lock();              try              {                    while(flag == false)                    {                      consumer_con.await();                                    }                    System.out.println(name+"---"+"消费者--------------"+num);                    flag = false;                    //this.notifyAll();//唤醒生产者                    producer_con.signal();// 只唤醒 生产者线程不唤醒本方线程              }              finally              {                    lock.unlock();                  }    }}//生产者class Producer1 implements Runnable{    private Product1 product;    public Producer1(Product1 product)    {        this.product = product;    }    public void run()    {              while(true)      {            try{                product.put();                }            catch(InterruptedException e)            {                                    }                      }    }    }//消费者class Consumer1 implements Runnable{    private Product1 product;    public Consumer1(Product1 product)    {        this.product = product;    }    public void run()    {        while(true)       {            try            {              product.get();            }            catch(InterruptedException e)            {                            }       }    }}public class producerConsumerDmeo2 {    public static void main(String[] args) {        // TODO Auto-generated method stub        Product1 product = new Product1("商品");                Producer1 producer = new Producer1(product);        Consumer1 consumer = new Consumer1(product);                Thread t1 = new Thread(producer);        Thread t2 = new Thread(producer);        Thread t3 = new Thread(consumer);        Thread t4 = new Thread(consumer);                t1.start();//问题的产生:当存在多个生产者,多个消费者时:一个商品被2个消费者消费        t2.start();        t3.start();        t4.start();       }

四 线程中的常用函数

线程停止的方法

stop():已过时

interrupt();

如何停止一个线程:

  从本质上说:只有一个方法:结束Run方法。

当开启一个线程时:线程运行的通常是循环结构的代码,所以要结束run();只要控制住循环。

特殊情况:

  当线程处于冻结状态。
  就不会读取到标记。那么线程就不会结束

当没有指定的方式来让冻结的线程回复运行状态时,这时需要对冻结进行清除。
强制让线程回复到运行状态中来,这样就可以操作标记让线程结束。

interrupt:清除线程的冻结状态
调用一个线程的interrupt()方法时会触发InterruptedException 异常

在异常处理函数中修改循环条件。

setDaemo();

t1.setDaemon();让t1成为守护线程(后台线程);当前台线程(创建t1的线程)结束时t1会自动结束

注意:该方法必须在线程开启之前调用

join()方法

当A线程,执行到了B线程的.join()方法时,A就会等待B线程执行完
A才会执行。
join可以用来临时加入线程。


线程优先级
t.setPriority(int )//优先级 只有10级
1:MIN_PRRORITY
5:NORMAL_PROORITY
10:MAX_PRORITY    

yield() 方法;
Thread.yield();//线程会释放执行权。类似sleep();



1 0
原创粉丝点击