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

来源:互联网 发布:淘宝客越来越难做 编辑:程序博客网 时间:2024/06/03 16:26

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

一、概述

进程:指正在运行的程序,负责这个程序的内存空间分配,代表该程序在内存中的执行区域。

线程:就是在一个进程中负责一个执行路径。

多线程:就是在一个进程中多个执行路径同时执行。

多线程的好处:

1. 一个进程里面可以同时运行多个任务。

2. 提供资源的利用率。

多线程的弊端:

1. 降低了一个进程里面的线程的执行频率。

2. 对线程进行管理要求额外的 CPU开销,会给系统带来上下文切换的额外负担。

3. 当多个线程需要对公有变量进行写操作时,可能发生线程安全问题。

4. 线程的死锁。

线程的状态:

创建:新创建了一个线程对象。

可运行:线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取cpu的执行权。

运行:就绪状态的线程获取了CPU执行权,执行程序代码。

阻塞: 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。

死亡:线程执行完它的任务时。

二、创建线程的方式

1、继承Thread类

步骤:

    1. 定义一个类继承Thread类。
    2. 覆盖Thread类中的run方法。
    3. 直接创建Thread的子类对象创建线程。
    4. 调用start方法开启线程并调用线程的任务run方法执行。

练习:

class Demo extends Thread{      private String name ;      Demo(String name){             this.name = name;      }       public void run(){             for(int x = 0; x < 10; x++){                  System.out.println(name + "...x=" + x + "...ThreadName=" + Thread.currentThread ().getName());            }      }}class ThreadDemo{       public static void main(String[] args){            Demo d1 = new Demo("线程1");            Demo d2 = new Demo("线程2");            //开启线程,调用run方法。            d1.start();             d2.start();            for(int x = 0; x < 20; x++){                  System.out.println("x = " + x + "...over..." + Thread.currentThread().getName());            }      }}

2、实现Runnable接口

避免了Java单继承的局限性,创建线程的第二种方式较为常用。

步骤:

1:定义了实现Runnable接口

2:重写Runnable接口中的run方法,就是将线程运行的代码放入在run方法中

3:通过Thread类建立线程对象

4:将Runnable接口的子类对象作为实际参数,传递给Thread类构造方法

5:调用Thread类的start方法开启线程,并调用Runable接口子类run方法

//需求:简单的卖票程序。多个窗口卖票。class Ticket implements Runnable{private  int tick = 100;public void run(){while(true){if(tick>0){//显示线程名及余票数System.out.println(Thread.currentThread().getName()+":sale -- "+ tick--);}}}}class  TicketDemo{public static void main(String[] args) {//创建卖票线程实例对象Ticket t = new Ticket();//四个线程代表四个窗口在卖票Thread t1 = new Thread(t);Thread t2 = new Thread(t);Thread t3 = new Thread(t);Thread t4 = new Thread(t);                //启动线程                t1.start();                t2.start();                t3.start();                t4.start();        }}
三、线程安全

线程安全问题产生的原因:当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致线程安全问题的产生。
    1. 多个线程在操作共享的数据。

    2. 操作共享数据的线程代码有多条。

解决办法:

对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。一种是同步代码块,还有就是同步函数。都是利用关键字synchronized来实现。

1、同步代码块

class Ticket implements Runnable{      private int num = 100;      Object obj = new Object();      public void run(){             while(true ){                   synchronized(obj ){//要同步的代码块                         if(num > 0){                              System.out.println(Thread.currentThread().getName() + "...sale..." + num--);                        }                   }              }      }}class TicketDemo{       public static void main(String[] args){            Ticket t = new Ticket();            Thread t1 = new Thread(t);            Thread t2 = new Thread(t);            Thread t3 = new Thread(t);            Thread t4 = new Thread(t);                        t1.start();            t2.start();            t3.start();            t4.start();      }}

2、同步函数

class Bank{       private int sum ;       public synchronized void add(int num){ //同步函数             sum = sum + num;             System.out.println("sum = " + sum);       }}

同步函数和同步代码块的区别:

    1. 同步函数的锁是固定的this。
    2. 同步代码块的锁是任意的对象。
    建议使用同步代码块。
静态函数的同步方式:
//加同步的单例设计模式————懒汉式class Single{private static Single s = null;private Single(){}public static void getInstance(){if(s==null){synchronized(Single.class){if(s==null)s = new Single();}}return s;}}
死锁出现情况:

1:两个任务以相反的顺序申请两个锁,死锁就可能出现

2:线程T1获得锁L1,线程T2获得锁L2,然后T1申请获得锁L2,同时T2申请获得锁L1,此时两个线程将要永久阻塞,死锁出现

四、线程通讯

多个线程操作同一资源,但操作动作不一样需要线程通讯

1、使用同步操作同一资源

//多消费者和生产者//资源class Resource{       private String name ;       private int count = 1;       private boolean flag = false;       public synchronized void set(String name){            while(flag )                   try{                                          //让线程处于冻结状态,被wait的线程会被存储到线程池中。                         this.wait();                  } catch(InterruptedException e){                        e.printStackTrace();                  }             this.name = name + count;             count++;             System.out.println(Thread.currentThread().getName() + "...生产者..." + this. name);             flag = true ;             //唤醒线程池中所有线程             notifyAll();      }       public synchronized void out(){             while(!flag )                   try{                         this.wait();                  } catch(InterruptedException e){                        e.printStackTrace();                  }            flag = false ;            notifyAll();            System.out.println(Thread.currentThread().getName() + "...消费者..." + this. name);      }}//生产者线程class Producer implements Runnable{       private Resource r ;      Producer(Resource r){             this.r = r;      }       public void run(){             while(true ){                   r.set( "烤鸭");            }      }}//消费者线程class Consumer implements Runnable{       private Resource r ;      Consumer(Resource r){             this.r = r;      }       public void run(){             while(true ){                   r.out();            }      }}class ProducerConsumerDemo {       public static void main(String[] args){            Resource r = new Resource();            Producer pro = new Producer(r);            Consumer con = new Consumer(r);            Thread t0 = new Thread(pro);            Thread t1 = new Thread(pro);            Thread t2 = new Thread(con);            Thread t3 = new Thread(con);            t0.start();            t1.start();            t2.start();            t3.start();      }}

2、JDK1.5新特性

    Lock接口:替代同步代码块或者同步函数,将同步的隐式操作变成显示锁操作。可以一个锁上加上多组监视器。
    lock():获取锁。
    unlock():释放锁,为了防止异常出现,导致锁无法被关闭,所以锁的关闭动作要放在finally中。

    Condition接口:出现替代了Object中的wait、notify、notifyAll方法。将这些监视器方法单独进行了封装,变成Condition监视器对象,可以任意锁进行组合。
    Condition接口中的await方法对应于Object中的wait方法。
    Condition接口中的signal方法对应于Object中的notify方法。
    Condition接口中的signalAll方法对应于Object中的notifyAll方法。

   练习: 使用一个Lock、一个Condition修改上面的多生产者-多消费者问题。
import java.util.concurrent.locks.*;class Resource{       private String name ;       private int count = 1;       private boolean flag = false;             //创建一个锁对象       Lock lock = new ReentrantLock();       //通过已有的锁获取该锁上的监视器对象             Condition con = lock .newCondition();       //通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者       Condition producer_con = lock .newCondition();       Condition consumer_con = lock .newCondition();       public void set(String name){             lock.lock();             try{                   while(flag )                         try{                              producer_con.await();                        } catch(InterruptedException e){                              e.printStackTrace();                        }                   this.name = name + count;                   count++;                   System.out.println(Thread.currentThread().getName() + "...生产者..." + this. name);                   flag = true ;                   consumer_con.signal();            } finally{                   lock.unlock();            }      }       public void out(){             lock.lock();             try{                   while(!flag )                         try{                               consumer_con.await();                        } catch(InterruptedException e){                              e.printStackTrace();                        }                   flag = false ;                   producer_con.signal();                   System.out.println(Thread.currentThread().getName() + "...消费者..." + this. name);            } finally{                   lock.unlock();            }      }}class Producer implements Runnable{       private Resource r ;       Producer(Resource r){             this.r = r;       }       public void run(){             while(true ){                   r.set( "烤鸭");            }      } }class Consumer implements Runnable{       private Resource r ;       Consumer(Resource r){             this.r = r;       }       public void run(){             while(true ){                   r.out();            }      }}class ProducerConsumerDemo {       public static void main(String[] args){            Resource r = new Resource();            Producer pro = new Producer(r);            Consumer con = new Consumer(r);            Thread t0 = new Thread(pro);            Thread t1 = new Thread(pro);            Thread t2 = new Thread(con);            Thread t3 = new Thread(con);            t0.start();            t1.start();            t2.start();            t3.start();      }}

五、停止线程

JDK1.5之前使用stop()方法,升级后在run()方法内控制,是run()方法结束
例如:
public  void run(){while(flag){System.out.println(Thread.currentThread().getName()+"....run");}}

2、当线程处于冻结状态。就不会读取到标记。那么线程就不会结束。需要对冻结进行清除。强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。Thread类提供该方法interrupt();

六、其他小方法

1、join方法

        当A线程执行到了b线程的.join()方法时,A线程就会等待,等B线程都执行完,A线程才会执行。(此时B和其他线程交替运行。)join可以用来临时加入线程执行。

2、setPriority()方法用来设置优先级

        MAX_PRIORITY 最高优先级10

        MIN_PRIORITY   最低优先级1

        NORM_PRIORITY 分配给线程的默认优先级

3、yield()方法可以暂停当前线程,让其他线程执行。


0 0