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()方法执行时,会阻塞,直到其返回。
- 【Java多线程】多线程死锁
- Java 多线程
- java 多线程
- java多线程
- JAVA多线程
- java多线程
- JAVA多线程
- java多线程
- JAVA 多线程
- Java多线程
- java多线程
- JAVA 多线程
- Java 多线程
- Java 多线程
- java多线程
- Java 多线程
- Java多线程
- java 多线程
- js获取元素相对于父元素的位移,获取元素本身的宽高
- 程序员眼里的高迸发
- js 学习总结,可利用其与原生代码交互
- ZOJ 3932 Handshakes
- 抽屉效果
- Java多线程
- Android常用系统广播
- 鼠标拉近旋转拖拽模型
- html5+css(3)
- 第三次上机作业
- 快速幂算法初尝试
- 2016-4-4至2016-4-10
- 一句话理清service和activity 通信的机制
- Mac本地生成SSH Key 的方法