Java基础-多线程-①线程的建立和启动

简单阐释进程和线程java

对于进程最直观的感觉应该就是“windows任务管理器”中的进程管理:编程

  (计算机原理课上的记忆已经快要模糊了,简单理解一下):一个进程就是一个“执行中的程序”,是程序在计算机上的一次运行活动。程序要运行,系统就在内存中为该程序分配一块独立的内存空间,载入程序代码和资源进行执行。程序运行期间该内存空间不能被其余进程直接访问。系统以进程为基本单位进行系统资源的调度和分配。何为线程?线程是进程内一次具体的执行任务。程序的执行具体是经过线程来完成的,因此一个进程中至少有一个线程。回忆一下 HelloWrold 程序中main方法的执行,其实这时候,Java虚拟机会开启一个名为“main”的线程来执行程序代码。一个进程能够包含多个线程,这些线程共享数据空间和资源,但又分别拥有各自的执行堆栈和程序计数器。线程是CPU调度的基本单位。windows

多线程多线程

  一个进程包含了多个线程,天然就叫作多线程。拥有多个线程就可让程序看起来能够“同时”处理多个任务,为何是看起来呢?由于CPU也分身乏术,只能让你这个线程执行一下子,好了你歇着,再让另外一个线程执行一下子,下次轮到你的时候你再继续执行。这里的“一下子”实际上时间很是短,感受上就是多个任务“同时”在执行。CPU就这样不停的切来切去…既然CPU一次也只能执行一个线程,为何要使用多线程呢?固然是为了充分利用CPU资源。一个线程执行过程当中不可能每时每刻都在占用CPU,CPU歇着的时候咱们就可让它切过来执行其余的线程。ide

  好比QQ聊天的时候,跟一我的正聊着呢,另外一个消息过来了。若是是单线程,很差意思,等我跟这一个聊完说拜拜以后再去理你吧。多线程呢,消息窗口全打开,这个窗口说完话了,总得等人家回吧,趁这个空闲时候,处理另外一个窗口的消息。这样看起来不就是同时进行了么,每个窗口的另外一边都觉得你只在跟他一我的聊天…函数

Java中的多线程post

  Java中启用多线程有两种方式:①继承Thread类;②实现Runnable接口this

There are two ways to create a new thread of execution. One is to declare a class to be a subclass of Thread. This subclass should override the run method of class Thread. An instance of the subclass can then be allocated and started.The other way to create a thread is to declare a class that implements the Runnable interface. That class then implements the run method. An instance of the class can then be allocated, passed as an argument when creating Thread, and started. spa

继承Thread类操作系统

  建立一个类,继承java.lang.Thread,并覆写Thread类的run()方法,该类的实例就能够做为一个线程对象被开启。

/**
 * Dog类,继承了Thread类
 * @author lt
 */
class Dog extends Thread {
    /*
     * 覆写run()方法,定义该线程须要执行的代码
     */
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
        }
    }
}

线程建立好了,怎么让它做为程序的一个独立的线程被执行呢?建立一个该类的实例,并调用start()方法,将开启一个线程,并执行线程类中覆写的run()方法。

public class ThreadDemo {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.start();
    }
}

看不出什么端倪,若是咱们直接调用实例的run()方法,执行效果是彻底同样的,见上图。

public class ThreadDemo {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.run();
    }
}

若是一切正常,这时候程序中应该有两个线程:一个主线程main,一个新开启的线程。run()方法中的代码到底是哪一个线程执行的呢?Java程序中,一个线程开启会被分配一个线程名:Thread-x,x从0开始。咱们能够打印当前线程的线程名,来看看到底是谁在执行代码。

class Dog extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("当前线程:" + Thread.currentThread().getName() + "---" + i);
        }
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        System.out.println("当前线程:" + Thread.currentThread().getName());
        Dog dog = new Dog();
        dog.start();
    }
}

能够看到,确实开启了一个新的线程:Thread-0,main()方法的线程名就叫main。

同一个实例只能调用一次start()方法开启一次,屡次开启,将报java.lang.IllegalThreadStateException异常:

咱们再建立一个实例,开启第三个线程:

public class ThreadDemo {
    public static void main(String[] args) {
        System.out.println("当前线程:" + Thread.currentThread().getName());
        Dog dog = new Dog();
        Dog dog2 = new Dog();
        dog.start();
        dog2.start();
    }
}

这时候咱们已经可以看到多线程的底层实现原理:CPU切换处理、交替执行的效果了。

run和start

  上面咱们直接调用run()方法和调用start()方法的结果同样,如今咱们在打印线程名的状况下再来看看:

public class ThreadDemo {
    public static void main(String[] args) {
        System.out.println("当前线程:" + Thread.currentThread().getName());
        Dog dog = new Dog();
        Dog dog2 = new Dog();
        dog.run();
        dog2.run();
    }
}

能够看到,这时候并无开启新的线程,main线程直接调用执行了run()方法中的代码。因此start()方法会开启新的线程并在新的线程中执行run()方法中的代码,而run()方法不会开启线程。查看start()的源代码,该方法调用了本地方法 private native void start0();即调用的是操做系统的底层函数:

public synchronized void start() {
    if (threadStatus != 0)
        throw new IllegalThreadStateException();

    group.add(this);

    boolean started = false;
    try {
        start0();
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
            /* do nothing. If start0 threw a Throwable then
              it will be passed up the call stack */
        }
    }
}

private native void start0();

实现Runnable接口

  第二种方式,实现Runnable接口,并覆写接口中的run()方法,这是推荐的也是最经常使用的方式。Runnable接口定义很是简单,就只有一个抽象的run()方法。

//Runnable接口源码
public interface Runnable {
    public abstract void run();
}
class Dog implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("当前线程:" + Thread.currentThread().getName() + "---" + i);
        }
    }
}

这时候的Dog类看起来跟线程什么的毫无关系,也没有了start()方法,怎么样开启一个新的线程呢?直接调用run()方法?想一想也不行。这时候咱们须要将一个Dog类的实例,做为Thread类的构造函数的参数传入,来建立一个Thread类的实例,并经过该Thread类的实例来调用start()方法从而开启线程。

public class ThreadDemo {
    public static void main(String[] args) {
        System.out.println("当前线程:" + Thread.currentThread().getName());
        Dog dog = new Dog();
        Thread thread = new Thread(dog);
        thread.start();
    }
}

这时候若是要开启第三个线程,须要建立一个新的Thread类的实例,同时传入刚才的Dog类的实例(固然也能够建立一个新的Dog实例)。这时候咱们就能够看到跟继承Thread类的方式的区别:多个线程能够共享同一个Dog类的实例。

public class ThreadDemo {
    public static void main(String[] args) {
        System.out.println("当前线程:" + Thread.currentThread().getName());
        Dog dog = new Dog();
        Thread thread = new Thread(dog);
        Thread thread2 = new Thread(dog);
        thread.start();
        thread2.start();
    }
}

两种方式的比较

  继承Thread类的方式有它固有的弊端,由于Java中继承的单一性,继承了Thread类就不能继承其余类了;同时也不符合继承的语义,Dog跟Thread没有直接的父子关系,继承Thread只是为了能拥有一些功能特性。而实现Runnable接口,避免了单一继承的局限性,同时更符合面向对象的编程方式,即将线程对象进行单独的封装,并且实现接口的方式下降了线程对象(Dog)和线程任务(run方法中的代码)的耦合性,如上面所述,可使用同一个Dog类的实例来建立并开启多个线程,很是方便的实现资源的共享。实际上Thread类也是实现了Runnable接口。实际开发中可能是使用实现Runnable接口的方式。

相关文章
相关标签/搜索