Java学习笔记-8.多线程编程

一、引入线程

1.多线程和多进程的区别

  (1)两者粒度不同,进程是由操作系统来管理,而线程则是在一个进程内

  (2)每个进程是操作系统分配资源和处理器调度的基本单位,拥有独立的代码、内部数据和状态

     而一个进程内的多线程只是处理器调度的基本单位,共享该进程的资源,线程间有可能相互影响

  (3)线程本身的数据通常只有寄存器数据,以及一个程序执行时使用的堆栈,所以线程的切换比进程切换的负担小

2.Thread类:Java的线程是通过java.lang.Thread类来实现,一个Thread对象代表一个线程

3.Runnable接口:只有一个方法run(),所有实现Runable接口的用户类都必须具体实现run()

   当线程被调度并转入运行状态时,它所执行的就是run()方法中定义的操作,所以,一个实现Runnable接口的类实际上是定义一个新线程的操作

   Thread类实现了Runnable接口

二、线程的实现

1.实现多线程:继承Thread类构造线程、实现Runnable接口构造线程

2.继承Thread类:必须覆写Thread类中的run()方法,run()方法中定义了线程要执行的代码

   定义语法:

class 类名称 extends Thread {
    属性:
    方法:
    //覆写Thread类中的run方法
    public void run() {
        线程主题;
    }
}

  继承Thread类创建和执行多线程步骤:

(1)定义一个类扩展Thread

(2)覆盖run() 方法,这个方法中实现线程中要执行的操作

(3)创建一个这个线程类的对象

(4)调用start() 方法启动线程对象

例:

class CountingThread extends Thread {
    private String name;
    public CountingThread(String name) {
        this.name = name;
    }
    public void run() {
        System.out.println("Thread start:" + this.name);
        for (int i = 0; i < 9; i++) {
            System.out.println(name + " run:" + (i + 1) + "\t");
        }
        System.out.println("Thread finish:" + this.name);
    }
}
class ThreadDemo {
    public static void main(String[] args) {
        CountingThread thread1 = new CountingThread("Thread A");
        CountingThread thread2 = new CountingThread("Thread B");
        thread1.start();
        thread2.start();
    }
}

   不能直接调用run() 方法的原因:当调用start() 方法时,系统启动线程,并分配虚拟CPU开始执行这个线程的run() 方法后,立即返回,而不是等到run()方法执行后返回

3.实现Runnable接口

  (1)定义一个类实现Runnable接口:implements Runnable

  (2)覆写其中的run() 方法

  (3)创建Runnable 接口实现类的对象

  (4)创建Thread类的对象(以Runnable子类对象为构造方法参数)

  (5)用start() 方法启动线程

例:

class CountingThread implements Runnable {
    private String name;
    public CountingThread(String name) {
        this.name = name;
    }
    public void run() {
        System.out.println("Thread start:" + this.name);
        for(int i = 0; i < 10; i++) {
            System.out.println(name + " run: i = " + i);
        }
        System.out.println("Thread end:" + this.name);
    }
}
public class RunnableDemo {
    public static void main(String[] args) {
        CountingThread ct1 = new CountingThread("Thread A");
        CountingThread ct2 = new CountingThread("Thread B");
        Thread thread1 = new Thread(ct1);
        Thread thread2 = new Thread(ct2);
        thread1.start();
        thread2.start();
    }
}

4.两种实现方式的对比

  (1)使用 Runnable 接口,可以避免由于Java的单继承带来的局限

  (2)实现Runnable接口适合多个相同程序代码的线程去处理同一资源的情况

  所以在开发中建议使用Runnable接口实现多线程

三、线程的调度

1.线程的生命周期

  (1)新建状态:Thread myThread = new MyThreadClass()

             当一个线程处于新建状态时,它仅仅是一个空的线程对象,系统不为它分配资源

  (2)就绪状态:也成为可运行状态(Runnable),方法:start()

             处于新建状态的线程被启动后,将进入线程队列排队等待CPU时间片

             此时它已经具备了运行的条件,一旦轮到它来享用CPU资源,就可以脱离创建它的主线程独立开始自己的生命周期

  (3)运行状态:当就绪状态的线程被调度并获得处理器资源时,便进入运行状态

                         当线程对象被调度执行时,它将自动调用本对象的 run() 方法,并顺序执行

  (4)阻塞状态:一个正在执行的线程如果在某些特殊情况下,不能执行线程的状态,将让出CPU

  (5)死亡状态:一个正常运行的线程完成了全部工作,即执行完了run()方法的最后一个语句

                         或线程被提前强制性执行,如通过执行 stop() 方法终止线程

2.线程的优先级:Java将线程的优先级分为10个等级,分别用1~10表示,数字越大表明级别越高

   Thread类中静态常量:

定义 描述 表示的常量
public static final int MIN_PRIORITY 最低优先级 1
public static final int NORM_PRIORITY 普通优先级,默认优先级 5
public static final int MAX_PRIORITY 最高优先级 10

   方法:setPriority(int p) :改变线程的优先级

            getPriority():获得线程的优先级

   优先级高的线程会获得较多的运行机会

四、线程的基本控制

1.线程睡眠:public static void sleep(long millis) throws InterruptedException

   当前线程将睡眠millis毫秒,可以使优先级低的线程得到执行的机会

2.线程状态测试:

   线程由start()方法启动后,直到其被终止之间的任何时刻,都处于活动状态

   可以通过Thread中的 isAlive() 方法来获取线程是否处于活动状态

   定义:public final boolean isAlive()

3.线程加入:public final void join() throws InterruptedException

   有一个A线程正在运行,希望插入一个B线程,并要求B线程先执行完毕,然后再继续A的执行

4.线程礼让:public static void yield()

   不能指定暂停多长时间,会将CPU的占有权交给具有相同优先级的线程,否则继续运行原线程

   注意:只能让同优先级的线程有执行机会

5.守护线程:不是应用程序的核心部分,当一个应用程序的所有非守护线程终止运行时,即使仍然有守护线程在运行,应用程序也将终止

   守护线程一般被用于在后台为其他线程提供服务

   方法:public final boolean isDaemon()    判断一个线程是否是守护线程

            public final void setDaemon()    将一个线程设为守护线程

五、多线程的同步与死锁

1.对象互斥锁:阻止多个线程同时访问一个条件变量,使用synchronized来声明一个操作共享数据的一段代码块或一个方法

  (1)同步代码块:任何时刻只能有一个线程能获得此代码块的访问权

          synchronized(<同步对象名>) {

                 <需要同步的代码>

          }

  (2)同步方法:任何时刻该方法只能被一个线程执行

          synchronized <方法返回值类型> <方法名>(<参数列表>) {

                 <方法体>

          }

2.线程间交互同步

等待通知机制:(1)在生产者没有生产之前,通知消费者等待,生产者生产后,马上通知消费者消费

                     (2)在消费者消费后,通知生产者已经消费完,需要生产

方法:

方法名称 描述
public final void wait() throws InterruptedException 释放已持有的锁,进入等待队列
public final void wait(long timeout) throws InterruptedException 指定最长的等待时间,单位为毫秒
public final void notify() 唤醒第一个等待的线程并把它移入锁申请队列
public final void notifyAll() 唤醒全部等待的线程并将它们移入锁申请队列

注意:

wait() 和 notify()/notifyAl()必须在已经持有锁的情况下执行,它们只能出现在synchronized作用的范围内

posted @ 2015-03-03 20:27  trj14  阅读(228)  评论(4编辑  收藏  举报