创建并启动 Java 线程

原文链接:http://tutorials.jenkov.com/java-concurrency/creating-and-starting-threads.html
和其他的 Java 对象一样, Java 线程也是对象。线程是 Java java.lang.Thread 类或者其子类的实例。除了作为一个普通的对象, Java 线程还可以执行代码。

创建和启动线程

在 Java 中,可以这样来创建一个线程:

    Thread thread = new Thread();

可以通过调用它的 start() 方法来启动线程:

    thread.start();

在这个例子中,没有定义线程执行的代码。线程启动后就会立刻停止。

有两种方法来定义线程执行的代码。第一种是创建一个 Thread 子类并重写父类的 run() 方法。调用 start() 方法后,线程会执行 run() 方法。下面是一个例子:

    public class MyThread extends Thread {
        public void main() {
            System.out.println("MyThread running");
        }
    }

只要线程启动以后, start() 方法调用会立刻返回,不会等到 run() 方法执行完才返回。 run() 方法执行时就像就在另外一个处理器上执行。当 run() 方法运行时会打印出文本“My Thread running”。

也可以像这样来创建一个匿名的 Thread 子类:

    Thread thread = new Thread() {
        public void run() {
            System.out.println("Thread Running");
        }
    }
    thread.start();

在这个例子中,新建的线程一旦执行 run() 方法后将会打印出文本“Thread running”。

Runnable 接口的实现

指定一个线程执行什么样的代码的第二种方式是创建一个实现了 java.lang.Runnable 接口的类。可以通过一个 Thread 对象来执行 Runnable 对象。

下面是一个 Java Runnable 例子:

    public class MyRunnable implements Runnable {
        public void run() {
            System.out.println("MyRunnable running");
        }

    } 

为了让 run() 方法被一个线程来执行,可以传递一个 MyRunnable 的实例给 Thread 类的构造方法。下面是具体做法:

    Thread thread = new Thread(new MyRunnable());
    thread.start();

当线程启动以后它将会执行 MyRunnable 实例的 run() 方法,而不会执行自己的 run() 方法。上面的例子中将会打印出文本“MyRunnable running”。

也可以像这样创建一个匿名的 Runnable 实现类:

    Runnable myRunnable = new Runnable() {
        System.out.println("Runnable runnig");
    }

    Thread thread = new Thread(myRunnable);
    thread.start();

继承自 Thread 还是实现 Runnable 接口?

两种方法哪种最好,没有定则,两种方法都可行。然而,我个人更偏爱实现 Runnable 接口,然后传递一个实现类的实例给 Thread 实例。当让 Runnable 的实现类通过一个线程池来执行的时候,来自线程池的一个线程空闲以后 Runnable 的实例更容易排队。用 Thread 的子类通过线程池来执行的话,要比 Runnable 的实现类困难。

有时候必须实现 Runnable 接口同时继承 Thread 类。例如,创建一个可以执行多个 Runnable 的 Thread 子类,这是实现线程池时典型的一种场景。

常见陷阱:直接调用 run() 方法而不是 start() 方法

创建并启动线程线程时一个常见的错误是像下面这样直接调用调用 Thread 的 run() 方法而不是 start() 方法:

    Thread newThread = new Thread(MyRunnable));
    newThread.run(); // 应该是调用 newThread.start();

起初,可能并没有发现什么异常因为 Runnable 的 run() 方法正如期望的那样执行了。然而, run() 方法并不是被刚才新建的线程执行的,而是被创建这个新线程的线程执行的。换句话说,执行 run() 方法的线程就是执行了以上两行代码的线程。为了让新建的线程 newThread 来执行 MyRunnable 实例的 run() 方法,必须通过调用 newThread.start() 方法来实现。

为了给大家证明这个结论,我写了段代码来重现这个现象。下面是我写的一个 Thread 的子类以及一个 main() 方法。

    public class MyThread extends Thread{
        public static void main(String[] args) {
            // 设置主线程的名字
            Thread.currentThread().setName("main thread");

            MyThread myThread1 = new MyThread();
            myThread1.setName("new thread1");
            MyThread myThread2 = new MyThread();
            myThread2.setName("new thread2");

            myThread1.run();
            myThread2.start();
        }

        @Override
        public void run() {
            String name = Thread.currentThread().getName();
            System.out.println(name);
        }
    } 

打印出来的结果是:

    main thread
    new thread2

由此可以证明调用 myThread1.run() 以后,执行 run() 方法的的确是 main 线程(即创建 myThread1 线程的线程)。

线程的名字

创建了一个线程以后可以给该线程起一个名字。这个名字可以帮助我们区别出不同的线程。例如,多个线程都通过 System.out 打印了文本,通过线程名字我们可以很方便的的看出是哪个线程打印的文本。下面是一个例子:

    Thread thread = new Thread("New Thread") {
        public void run() {
            System.out.println("run by" + getName());       
        }
    };
    thread.start();
    System.out.println(thread.getName);

注意字符串 “New Thread” 作为一个参数被传递给 Thread 的构造方法。这个字符串就是线程的名字。这个名字可以通过调用 Thread 的 getName() 方法来获取到。在使用 Runnable 的实现类的时候也可以传递一个名字给 Thread 的构造方法。下面是大概的代码实现:

    MyRunnable runnable = new MyRunnable();
    Thread thread = new Thread(runnable, "New Thread");

    thread.start();
    System.out.println(thread.getName());

然而,需要注意的是 MyRunnable 类不是 Thread 的子类,它不能访问当前执行线程的 getName() 方法。可以通过下面的方式获取到当前执行线程的引用:

    Thread.currentThread();

因此可以通过下面的方式来获取当前执行线程的名字:

    String threadName = Thread.currentThread().getName();

Java 线程的例子

下面是一个小例子。首先会打印出执行 main() 方法的线程的线程名字。该线程是由 JVM 分配的。然后,该线程会启动 10 个线程,给它们一个数字作为线程的名字。每个线程然后打印出自己的线程名字,最后执行完毕。

    public class ThreadExample {
        public static void main(String[] args){
            System.out.println(Thread.currentThread().getName());
            for(int i=0; i<10; i++){
                new Thread("" + i){
                    public void run(){
                        System.out.println("Thread: " + getName() + " running");
                    }
                }.start();
            }
        }
    }

注意,即使线程启动的顺序是(0,1,2,3,4等)但它们执行时可能不是顺序的,线程 0 可能不是第一个通过 System.out 打印出线程名字的线程。这是因为线程在原则上是并行执行而不是顺序执行。 JVM 或/和操作系统决定了线程执行的顺序。线程执行的顺序和它们的启动顺序没有必然的关联。

Next: 竞争条件和临界区

你可能感兴趣的:(创建java线程,启动Java线程)