码迷,mamicode.com
首页 > 编程语言 > 详细

《Java核心技术卷一》笔记 多线程

时间:2015-09-09 06:12:54      阅读:209      评论:0      收藏:0      [点我收藏+]

标签:

有时,我们需要在一个程序中同时并行的处理多个任务,如播放器一边要播放音乐同时还要不断更新画面显示,或者是一边执行耗时任务,UI还能一边继续响应各种事件。还有的时候,一个任务需要很长时间才能完成,如果分成多份一起执行,可以极大的缩短需要的时间。多线程可以很好的解决这类问题。

一个程序(进程)如果可以同时执行多个任务,每个并行的任务都是通过一个线程来完成,这就是一个多线程程序。进程拥有自己的一整套数据(变量),各个线程共享进程的数据,线程间通信比进程间通信更简单,线程开销比进程小。

Java中为多线程任务提供了很多的类。包括最基础的Thread类、Runnable等接口,用于线程同步的锁、阻塞队列、同步器,使用线程池的执行器、执行框架,还有可以在多线程中使用的线程安全集合等。

多线程基础

Runnable接口和Thread类是实现多线程最基础的方式。

1.创建线程

方式一:把要在新线程中执行的代码封装在Runnable对象的run()方法中,将Runable对象做为参数构造Thread对象,调用Thread对象的start()方法,并行任务就会在一个新的线程中开始执行,而当前线性继续执行后面的代码直至结束。

方式二:从Thead派生子类,重写Thread类的run()方法,在其中放置在要新线程中执行的代码,然后创建Thread子类对象并调用start()方法。这种方式可以省去Runnable包装类,但是却要创建一个Thread的子类。

推荐使用方法一,因为方法二把线程对象和具体任务一一绑定了,要执行1000个任务就得创建等量的线程,代价太大。应该把具体任务和用于执行任务的线程对象分离开,任务就封装在Runnbale对象中,这样就可使用线程池,用固定数量的线程来执行数量巨大的任务。

 

必须调用Thread类的start()方法才能开启新线程。直接调用Thread或Runnable对象的run()方法都只是在当前线程中执行任务。

 

Thread

    • Thread(Runnable target)      用Runnable对象构造Thread,Runnable对象会被保存在Thread的target域中
    • void run()      默认如果target域不为空,则执行target.run(),否则什么也不做。 可以继承Thread类,重写此方法,在其中放入要在新线程中执行的代码,不再需要Runanble对象封装新线程的任务代码。
    • synchronized void start()     创建一个新的线程并开始执行,JVM会在新线程中调用Thread对象的run()方法。一个Thread对象只能调用一次这个方法,并且会立即返回,调用完后两个线程就已经开始并行执行了。
    • static Thread currentThread()    返回正在执行这条命令的线程(当前线程)的Thread对象
    • void interrupt()   向线程发送中断请求,线程的中断状态被设置为true。特别说明见后文。
    • boolean isInterrupted()     测试线程中断状态是否被设置。不会改变中断状态。 
    • static boolean isInterrupted()   也是测试正在执行这条命令的线程(当前线程)是否被设置中断。调用后会将中断状态设置为false。

 

Runnable 接口    包装在新线程中执行的内容

    • void run()      新的线程创建后会执行该方法里的代码

示例代码

     class MyThread extends Thread
        {
            private int begin;
            public void setCountStart(int begin) { this.begin=begin; }
            @Override
            public void run()
            {
                try {
                    int end=begin+100;
                    for(int i=begin; i<end; i++){
                        System.out.println("sub: "+i);
                        Thread.sleep(1000);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        
        MyThread t=new MyThread();
        t.setCountStart(200);
        t.start();
        
        for(int i=0; i<100; i++){
            System.out.println("main: "+i);
            Thread.sleep(1000);
        }
        class MyTask implements Runnable
        {
            private int begin=0;
            public MyTask(int s){begin=s;}
            @Override
            public void run() {
                try {
                    int end=begin+100;
                    for(int i=begin; i<end; i++){
                        System.out.println("sub: "+i);
                        Thread.sleep(1000);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        
        Thread t=new Thread(new MyTask(123));
        t.start();
        
        for(int i=0; i<100; i++){
            System.out.println("main: "+i);
            Thread.sleep(1000);
            if(i==10)
                t.stop();
        }

 

2.终止线程

自动结束

Thread或Runnable对象的run()方法包装了新线程中执行的代码,在run()方法中遇到下面的情况,线程会自动终止。

  • 执行完最后一条语句
  • 遇到return返回
  • 出现未捕获的异常

 

强制结束

  • 调用Thread对象的stop()方法。抛出一个ThreadDeath异常(这个异常如果被捕获一定要重新抛出),停止线程执行。这个方法已经不推荐使用,原因是线程可能停止在一个不安全的状态,应该使用请求中断的方式。
  • 请求中断方式。要结束一个线程,就设置该线程的中断变量(调用Thread对象的interrupt()方法)(表明着有人想要中断这个线程),线程中的代码自己要负责查询中断变量(Thread类静态方法interrupted()或Thread对象的isInterrupted()方法),如果发现中断变量被设置了就自觉点不要再执行了,恢复到安全的状态后自行退出。请求中断不是强制的,如果线程中的代码不查询中断变量,或者发现中断变量已经被设置了但是不理会继续厚着脸皮执行,这个线程还是会一直运行不会被停止。

 

void interrupt()方法和InterruptedException特别说明

  • 如果调用该方法时,该线程正被某些可中断的方法阻塞着(sleep,wait或可中断IO调用等),那么现在肯定是无法检测中断状态的,清理中断状态立即抛出InterruptedException异常,阻塞的方法调用会立即被这个异常中断。
  • 如果调用该方法将中断状态设置为了true,紧接着就调用了一个可中断的方法(sleep,wait,可中断IO调用等),这个调用不会成功,并且同样会清除中断状态,抛出InterruptedException异常。可见,如果会循环调用sleep()这类可中断的方法,就不需要再手动检测中断状态了

(注意也存在不能被中断的阻塞IO调用,最好不要使用不可中断的方法)。

 

既然只要抛出InterruptedException,中断状态肯定已经被清理了,这种情况只有InterruptedException这个异常是我们知道有中断请求的唯一标识了,一定要向外层通知有中断发生,千万不要再把这个异常压制住,否则怎么调用interrupt()方法请求中断都不会有什么卵用,线程中外层的代码压根不知道有中断这回事,照常运行。将这个中断请求通知给外层有两种方式:

  • catch到InterruptedException时,调用Thread.currentThread().interrupt(),重新把中断状态设置上,让外层可以检测到。
  • 最好的方法是,不要再catch InterruptedException异常啦,只要有这个异常就往外层抛吧。一直抛到最外层,在Thread对象或Runnable对象的run()方法中处理这个异常。

 

直接调用stop()方法见前一段实例代码。采用中断方式实例如下:

        class MyInterruptableCheckTask implements Runnable
        {
            private int begin=0;
            public MyInterruptableCheckTask(int s){begin=s;}
            @Override
            public void run() 
            {
                int end = begin + 3000000;
                for (int i = begin; i < end && !Thread.currentThread().isInterrupted(); i++) {
                    System.out.println("sub: " + i);
                }

                if (Thread.currentThread().isInterrupted())
                    System.out.println("sub thread is interrupted");
                else
                    System.out.println("sub natural stop");
            }
        };
        
        Thread t=new Thread(new MyInterruptableCheckTask(111));
        t.start();
        
        for(int i=0; i<10; i++){
            System.out.println("main: "+i);
            Thread.sleep(1000);
            if(i==5)
                t.interrupt();
        }
class MyInterruptableExceptionTask implements Runnable
        {
            private int begin=0;
            public MyInterruptableExceptionTask(int s){begin=s;}
            @Override
            public void run() {
                try {
                    int end=begin+10;
                    for(int i=begin; i<end; i++){
                        System.out.println("sub: "+i);
                        Thread.sleep(1000);    //如果设置中断时正在sleep,或设置完中断后一个循环里遇到sleep,都会抛出InterruptedException异常,不需要再手动检测中断状态了
                    }
                } catch (InterruptedException e) {
                    System.out.println("the call Thread.sleep(n) is interrupted by InterruptedExcetpion");
                    Thread.currentThread().interrupt();    //产生InterruptedException异常时中断状态被清除,所有要重新设置中断或将中断异常向外抛出供后续代码检测是否发生了中断
                }        

                if(Thread.currentThread().isInterrupted())
                    System.out.println("sub thread is interrupted");
                else
                    System.out.println("sub natural stop");
            }
        };
        
        Thread t=new Thread(new MyInterruptableExceptionTask(111));
        t.start();
        
        for(int i=0; i<10; i++){
            System.out.println("main: "+i);
            Thread.sleep(1000);
            if(i==5)
                t.interrupt();
        }

 

 

 3.线程的状态

 

 

 

 

 

 

AWT事件分配线程

 

《Java核心技术卷一》笔记 多线程

标签:

原文地址:http://www.cnblogs.com/pixy/p/4790450.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!