Java多线程

来源:互联网 发布:毒药超女网络海选 编辑:程序博客网 时间:2024/06/16 17:13

实现方式

JAVA中,实现多线程主要有三种:继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程。前两种方式线程执行完后都没有返回值,最后一种是带返回值的。

继承Thread类

这种方式是很常见的多线程实现方式。通过Thread子类实例的start()方法来启动线程。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。

其实java.lang.Thread本质上也是实现了Runnable接口的一个实例,打开Thread类的源码如下。

public class java.lang.Thread implements java.lang.Runnable {
继承Thread,覆写run()。

public class SubThread extends Thread {private int name;public SubThread(int name) {this.name = name;}@Overridepublic void run() {System.out.println("Thread name : " + this.name); }}
之后我们就可以创建Thread实例,并通过start()启动线程。

public class Test {public static void main(String[] args) {for(int i = 0; i< 10; i++) {SubThread subT = new SubThread(i);subT.start();}}}

实现Runnable接口

在java中,很多情况下,都是多用接口,少用继承。至于为什么这么干,写多了你就明白了。打开Runnable接口发现源码很简单,里面只有一个run()。

public abstract interface java.lang.Runnable {    // Method descriptor #1 ()V  public abstract void run();}

public class SecondThread implements Runnable {private int name;public SecondThread(int name) {this.name = name;}@Overridepublic void run() {System.out.println("Thread name : " + this.name);}}

而我们只需要覆写改方法即可。只是启动线程时仍然需要一个Thread实例。

public class Test {public static void main(String[] args) {for(int i = 0; i< 10; i++) {SecondThread subT = new SecondThread(i);Thread thread = new Thread(subT);thread.start();}}}
所以从本质上讲,两种实现方式区别不大。

但有种情况值得注意,即两个Thread共享同一个target时。此时应考虑使用synchronized。

public class TestV {public static void main(String[] args) {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + ": " + i);SubRunnable threadB = new SubRunnable();new Thread(threadB, "threadB-1").start();new Thread(threadB, "threadB-2").start();}}}
public class SubRunnable implements Runnable {private int i;@Overridepublic void run() {for (; i < 100; i++) {System.out.println(Thread.currentThread().getName() + ": " + i);}}}
此时对于变量i的访问是线程不安全的。会造成输出跟我们期待的不一致。


Callable

第三种就是使用ExecutorService、Callable、Future了。这种方式最大的区别是它可以从任务中产生返回值。Callable是Java SE5引入的一种具有类型参数的泛型,它的类型参数表示的是从call()中返回的值。此外,它还要由ExecutorService的实例方法submit()来调用。因此它的实现与前两种还是有点区别的。

package com.zw;import java.util.concurrent.Callable;public class CallableThread implements Callable<String> {private String name;public CallableThread(String name) {this.name = name;}@Overridepublic String call() throws Exception {return "Result : " + this.name + ", id : "+ Thread.currentThread().getId();}}
这里让其返回String。

package com.zw;import java.util.ArrayList;import java.util.Date;import java.util.List;import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;public class Test {public static void main(String[] args) throws InterruptedException,ExecutionException {// 创建一个线程池ExecutorService pool = Executors.newCachedThreadPool();// 创建多个有返回值的任务List<Future> list = new ArrayList<Future>();for (int i = 0; i < 10; i++) {Callable c = new CallableThread(i);Date start = new Date();// 执行任务并获取Future对象Future f = pool.submit(c);Date end = new Date();System.out.println(end.getTime() - start.getTime());list.add(f);// 关闭线程池//pool.shutdown();}// 获取所有并发任务的运行结果for (Future re : list) {Date start = new Date();// 从Future对象上获取任务的返回值,并输出到控制台System.out.println(">>>" + re.get().toString());Date end = new Date();System.out.println(end.getTime() - start.getTime());}//for循环中的所有子线程都返回时,才会继续往下执行。System.out.println("All Sub Thread has beed done.");}}

这里re.get()还没有返回时,则发生阻塞。

下面进行验证。

在函数call()中让线程sleep一段时间。

@Override      public String call() throws Exception {    Thread.sleep(this.name * 1000);        return "Result : " + this.name + ", id : "+ Thread.currentThread().getId();      }


在ExecutorService的submit()前后进行时间计算。

Date start = new Date();// 执行任务并获取Future对象Future f = pool.submit(c);Date end = new Date();System.out.println(end.getTime() - start.getTime());
在Future的实例方法get()前后计算时间。

// 获取所有并发任务的运行结果for (Future re : list) {Date start = new Date();// 从Future对象上获取任务的返回值,并输出到控制台System.out.println(">>>" + re.get().toString());Date end = new Date();System.out.println(end.getTime() - start.getTime());}

结果


第一部分输出,时间都很短,说明submit()只是把线程放到线程池中。

第二部分输出很慢,证明get()方法执行时,会阻塞,直到其返回。







0 0
原创粉丝点击