Java攻城狮的入门课程程序员

(一)多线程基础

2017-03-19  本文已影响99人  黒猫

1、基本概念

1.进程:正在进行中的程序,指程序在内存中开辟了一块空间;进程持有资源(共享内存、共享文件)和线程,具有动态性。

2.线程:负责程序执行的一条执行路径,该路径也被称为执行单元。进程的执行实际上是线程在执行,一个进程至少会有一个线程,当一个进程中有多个线程时,就是多线程程序,这些线程共享进程资源。

3.多线程意义:最主要的目的是实现同时执行的不同功能的效果,虽然不一定能提高效率,但可以合理的利用cpu资源。

4.任务:每个线程需要执行的代码被称为任务代码,且都有其存储位置。例如下面代码示例中:

主线程的任务代码存储在main()方法中
垃圾回收线程的任务代码存储在finalize()方法中

线程是随着任务代码的执行才存在的,且随着任务代码的结束而消失。


2、线程示例

通过利用垃圾回收机制证明JVM虚拟机是多线程程序

//创建该类用于产生垃圾
class garbage {
    /*
     * 考虑到所有的对象都可以被当做垃圾回收 
     * 因此垃圾回收机制的代码应该在所有类的父类的当中 
     * 即Object类中的finalize()方法
     * 重写该方法便于验证是否执行
     */
    public void finalize() {
        System.out.println("垃圾回收线程抢占CPU成功!");
    }
}

public class Demo1 {

    public static void main(String[] args) {
        /*
         * main()方法是程序的入口 
         * 因此以下代码都属于主线程部分
         */
        
        /*
         * 创建多个匿名对象 
         * 匿名对象在创建后就直接成为了垃圾 
         * 因此可触发垃圾回收机制
         */
        new garbage();
        new garbage();
        new garbage();
        new garbage();
        new garbage();

        /*
         * 因为垃圾量少且垃圾回收机制的优先级本身就低 
         * 因此使用System类中的强制垃圾回收方法gc() 
         * 这样主线程运行至此就会启动垃圾回收线程
         * 执行finalize()方法
         */
        System.gc();// 垃圾回收线程启动,此时同时存在2个线程
        System.out.println("主线程抢占CPU成功!");

    }

}

结果如下:
由于线程之间在争抢cpu,因此多线程程序的执行结果是不确定的,这就是多线程程序的随机性。


3、Thread类

在Java中,线程同样实现了面向对象,那就是Thread类,使用该类创建新执行线程有两种方法:

1.将类声明为Thread的子类。该子类重写Thread类的run()方法。接下来可以分配并启动该子类的实例。如果直接创建Thread类的对象并用对象调用start()方法,将不会有任何结果,因为任务代码要求必须写在run()方法中,而Thread类中的run()方法没有任何功能。
该方法不建议使用,仅了解即可!


// 实现两位学生同时回答问题的效果

class Student extends Thread {
    // 创建Student类继承Thread类
    private String name;

    public Student() {
        super();
    }

    public Student(String name) {
        super();
        this.name = name;
    }

    public void run() {
        /*
         * 之前讲过 
         * 每个线程需要执行的代码被称为任务代码,且都有其存储位置
         * 此处重写run()方法 
         * 就是创建任务代码
         * 因此run()方法也就是任务代码的存储位置
         */
        for (int i = 1; i <= 10; i++) {
            System.out.println(name + "回答了第" + i + "个问题");
            /*
             * currentThread()是一个静态方法 
             * 返回值是当前正在执行的线程的对象 
             * 再调用对象的getName()方法
             * 显示当前线程的名称
             */
            // System.out.println(Thread.currentThread().getName()+"回答了第"+i+"个问题");
        }
    }

}

public class Demo2 {

    public static void main(String[] args) {
        /*
         * 由于Student类继承了Thread类 
         * 每次创建Student子类对象时 
         * 就相当于新创建了一个线程
         */
        Student s1 = new Student("Tom");
        /*
         * 主线程运行至创建s2对象 
         * 此时整个程序只有2个线程 
         * 主线程以及垃圾回收线程 
         * 两个子线程目前仅仅是创建 
         * 尚未启动,因此不会抢占cpu
         */
        Student s2 = new Student("Mike");
        /*
         * run()方法只是普通的方法调用 
         * 而start()方法可以启动线程 
         * 主线程的任务代码存储在main()方法中
         * 子线程的任务代码存储在run()方法中
         */
        s1.start();// 主线程执行至此,同时存在3个线程
        s2.start();// 主线程执行至此,同时存在4个线程

        System.out.println("提问!");
        // 显示主线程名称
        // System.out.println(Thread.currentThread().getName()+"提问!");

    }

}

注意:
  线程与线程之间在内存中是相互独立的,例如当一个线程发生异常时,其他线程不受影响,会继续执行。每个线程在栈中都有一块内存,当线程执行完自己的任务代码,就从栈中出栈,当所有线程都结束了,整个进程才会结束。

结果如下:
虽然主线程在执行最后一行输出代码之前已经启动了子线程,但子线程并未成功争抢到cpu,因此结果是主线程持续抢占cpu打印了“提问!”,之后才是子线程开始互相争抢cpu,这就表示子线程并不是随着启动就能占有cpu。

显示线程名称的结果如下:
主线程名字:main
子线程名字:Thread-编号,编号从0开始

2.声明实现Runnable接口的类,并重写run()方法,该类只是为了描述线程任务,实现了线程任务的面向对象,从而实现了线程任务和线程对象的分离,使线程执行的任务更加灵活,只要是实现了Runnable接口的类的对象,都可以作为参数传给Thread类的构造方法并启动线程;而且该类实现了接口的同时,还可以继承其他父类。
创建线程常使用此方法!

//实现四个窗口同时买票
class Ticket implements Runnable {
    // 创建Ticket类实现了Runnable接口

    // 定义总共50张票
    private int num = 50;

    // 重写Runnable接口中的run()方法,省略循环
    public void run() {
        if (num > 0) {
            System.out.println("窗口" + Thread.currentThread().getName() + "卖了第" + num-- + "张票");
        }
    }
}

public class Demo3 {

    public static void main(String[] args) {
        // 创建实现了Runnable接口的Ticket类的对象
        Ticket t = new Ticket();
        /*
         * 创建Thread类的对象,也就是创建了线程 
         * 把实现了Runnable接口的Ticket类的对象作为参数传递给Thread类的构造方法
         */

        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();

    }

}

4、线程的生命周期


版权声明:欢迎转载,欢迎扩散,但转载时请标明作者以及原文出处,谢谢合作!             ↓↓↓
上一篇下一篇

猜你喜欢

热点阅读