21.JAVA核心技术—多线程

1.线程:它是一个并发执行的顺序流,一个进程包括多个顺序执行流程,这个执行流程称为线程。

           线程是由操作系统创建并维护的一个资源,JVM就是一个进程。对于单个CPU来说。某个时刻只有一个线程在运行。

           线程由CPU分配给线程的时间片、线程代码、线程数据三个部分组成。

           线程与进程区别:进程是独立的数据空间,线程是共享的数据空间。//main也是一个线程为主线程。

  注意:a.线程和线程对象时两码事,线程对象能够到底层去申请一个线程资源。

          b.进程的调度室由OS负责。

2.线程的两种实现方式:实现Runnable接口和继承Thread类。

    第一种:class MyThread extends Thread{

                     public void run(){//需要执行的代码}

               }

               public class TestThread{

                     public static void main(String[] args){Thread t=new MyThread(); t.start();}

               }

    //只有等到所有的线程结束,进程才结束。

   第二种:class MyRunnable implements Runnable{ 
                    public void run(){(){//需要执行的代码}}
               }

              public class TestThread{
                     public static void main(String[] args){
                          Runnable r = new MyRunnable();
                          Thread t1 = new Thread(r);         t1.start();        
             }}

3.线程中断

   jdk1.0中存在一个stop方法,其他线程可以调用它来终止线程,不过现在已经被抛弃,现在可以用interrupt方法来请求终止一个线程。当interrupt方法被调用时,该线程的中断状态将会被置位。

  我们可以首先调用静态的Thread.currentThread方法取得当前线程,然后调用它的isInterrupted方法判断中断状态是否被置位了。

  若一个线程被阻塞了,它就无法检查中断状态,这就是产生InterruptedException异常的地方。所以我们如果每次工作后都调用sleep方法,那么这个isInterrupted检查就不是必须的了。

  当在一个被阻塞的线程上调用interrupt方法时,阻塞调用就会被InterruptedException异常所终止。

  注意:interrupted和isInterrupted的方法的区别?

     interrupted是一个静态方法,它检查当前线程是否中断,调用这个方法会清除该线程的中断状态。

     isInterrupted是一个实例方法,用来检查是否有线程已被中断,调用它不会改变中断状态的值。

4.线程状态

   4个状态:看起来很难理解   新生 可运行 阻塞 死亡

   5个状态:初始状态 可运行状态 运行状态 阻塞状态 死亡状态

   为了便于理解将锁池状态和等待队列从阻塞状态中分离出来,分为7中状态:初始状态 可运行状态 运行状态 阻塞状态 死亡状态 锁池状态 等待队列    如下图                      
                 ┌--------------------< 阻塞             死亡
                 ↓                   OS调度         ↑             ↑
    初始-------> 可运行< ---------------> 运行 >-----------┤
    t.start()启动 ↑        cpu到期或调用yield  ↓          ↓o.wait()
                 └-----< 锁池 ←---------<┘←-------< 等待队列
                  获得锁标志   synchronized(o)

   对七个状态进行解释:a.初始状态:创建线程,线程对象调用start方法。b.可运行:等待获得cpu资源 c.运行:获得cpu资源

                               d.阻塞:就是让出cpu资源,不是可运行态而是等待态  e.死亡:线程运行结束。

                               f.锁池:获得锁标志   g.等待队列:等待锁标志

    何时会阻塞状态:a.等待数据输入(输入设备进行处理,而CPU不处理),则放入阻塞,直到输入完毕,阻塞结束后会进入可

                             运行状态。如果优先级比当前正在运行的线程优先级高就会抢占当前线程的cpu资源。

                           b.线程休眠,线程对象调用sleep()方法,阻塞结束后会进入可运行状态。如果优先级比当前正在运行的线

                              程优先级高就会抢占当前线程的cpu资源。

                           c.线程对象2调用线程对象1的join()方法,那么线程对象2进入阻塞状态,直到线程对象1中止。

5.线程属性

 1)线程优先级:每个线程都有优先级,可以通过Thread.setPriority(int)设置线程的优先级

         Thread类:

               static int MIN_PRIORITY 线程的最低优先级 值我1

               static int NORM_PRIORITY 线程的默认优先级 值为5

               static int MAX_PRIORITY 线程的最高优先级 值为10

               static void yield()导致当前执行线程进入让步状态,如果有优先级高于它的可运行线程,那么这些线程将会被调度

 2)守护线程:主要是为其他线程提供服务,当只剩下守护线程,虚拟机就会退出。t.setDaemon(true)设置守护线程。

 3)线程组:比如有大量的线程试图从服务器上下图片,此时想中断当前页面的载入,这个时候我们就要中断所有这些线程,如

                果同时中断这些线程,我们就可以用线程组了。

               ThreadGroup g=new ThreadGroup(groupName)//字符串groupName用来标识该组的

               Thread t=new Thread(g,threadName)//创建线程并指定线程组

               g.interrupt()//中断线程组中的所有线程。

 4)未捕获异常处理器:线程的run方法不能抛出任何已检查的异常,但是未检查异常可以导致线程终止。然而并不需要任何

                               catch字句处理未检查异常,在死亡前,异常将被传递给未捕获异常的处理器处理。

           为线程安装处理器:setUncaughtExceptionHandler()为任何线程安装处理器;

                                    Thread.setDefaultUncaughtException()为所有线程安装一个默认的处理器。

6.线程同步

   从jdk1.5开始,有两种机制来保护代码块不受并行访问的干扰。一个是通过synchronized关键字;一个是ReentrantLock类,

   该类实现了Lock接口。

   1)synchronized关键字:Java中每个对象都有个隐式的锁,一个方法用synchronized声明,那么对象的锁将保护这个方法

                                 要调用这个方法,线程必须先获得对象的锁。

                   public synchronized void method(){........}//同步方法

                   public void method(){

                         synchronized(obj){......}//同步代码块,obj是我们在这个类中定义的锁对象。

                    }

           Object类:  notifyAll()//解除在该对象上调用wait的线程的阻塞状态,这个方法这能在同步方法或同步快用。

                          notify()//随机选择一个在该对象上调用wait的线程,解除它的阻塞状态。

                          wait()//导致线程进入等待状态直到被通知

    2)ReentrantLock类实现同步:

                     public void transfer(int from,int to,int amount){

                         rlock.lock();

                         try{......}

                         finally{rlock.unlock();}

                     }

                     private Lock rlock=new ReentrantLock();

         java.util.concurrent.locks.Lock接口:void lock()//获得这个锁,若这个锁被另一个线程持有,就发生阻塞。

                                                         void unlock()//释放这个锁

          java.til.concurrent.locks.ReentrantLock类:ReentrantLock()//构建一个可被用来保护临界区的可重入锁。

    3)条件对象:它是来管理那些已获得了锁却不能开始执行有用的工作的线程。

         Condition sufficientFunds=rlock.newCondition();

         public void transfer(int from,int to,int amount){

                         rlock.lock();

                         try{  while(...){   sufficientFunds.await();....... }

                                 sufficientFunds.signalAll();

                         }

                         finally{rlock.unlock();}

                     }

                     private Lock rlock=new ReentrantLock();

          Lock接口: Condition newCondition()//返回与该锁相关的一个条件对象。

          Condition接口:void await()//把该线程放到条件的等待集中。

                                void signalAll()//解除该条件的等待集中所有线程的阻塞状态。

                                void signal()//在该条件的等待集中随机选择一个线程,解除其阻塞状态。

   4)volatile关键字:为对一个实例的域的同步访问提供了一个免锁机制,如果我们把域声明为volatile,那么编译器和虚拟机就知道该域可能会被另一个线程并发更新。

   5)锁测试和超时:我们一般都是调用lock方法来获得一个锁,但是有时候会发生阻塞,所以我们可以通过调用trylock方法试图来获得一个锁,入股成功就返回

                           true,否则false,并且线程可以继续做其他的事。

                   if(mylock.trylock()){

                         try{......}finally{mylock.unlock();}......

                   }else{.....}

              我们也可以给trylock方法带一个超时参数:trylock(100,TimeUnit.MILLTSECONDS)

    6)读写锁:ReentrantReadWrite rwl=new ReentrantReadWrite();

                   读锁:Lock readlock=rwl.readLock();

                   写锁:Lock writelock=rwl.writeLock();

8.执行器和同步器

   我们往往会创建一些生存期限短的线程,此时我们就应该使用线程池,而执行器Executor类可以来构建线程池。

 Executors类的工厂方法:newCachedThreadPool():构建一个线程池,对于每个任务,若有空闲线程可用,立即让他执行,否则创建一个新线程

                                 newFixedThreadPool():构建一个大小固定的线程池,若提交的任务数量发育空闲线程数,那么得不到服务的任务将被置于队列中,当其

                                                                  他任务执行完后他们就能运行了

                                 newSingleThreadExecutor():是一个退化了的大小为1的线程池。由一个线程执行所有任务,一个接一个。

    /*这三个方法返回一个实现了ExecutorService接口的ThreadPoolExecutor类的对象*/

    使用连接池时应该做的事:调用Executors类中静态方法,调用submit来提交一个Runnable过Callable对象,若希望能够取消任务或若提交了一个Callable对

     象,那就保存好返回的Future对象。当不想再提交任何任务时调用shutdown。

                                 newScheduledThreadPool():

                                 newSingleThreadScheduledExecutor():

  结语:执行器、同步器看了一下没有看明白,想了想决定先放下,以后遇到的时候再看。

 

posted on 2012-04-25 10:04  ssy黑桃a  阅读(355)  评论(0编辑  收藏  举报