一、多线程

线程是指进程中的一个执行流程,一个进程中可以有多个线程。如java.exe进程中可以运行很多线程。进程是运行中的程序,是内存等资源的集合,线程是属于某个进程的,进程中的多个线程共享进程中的内存。线程之间的并发执行是线程轮流占用资源执行的结果,给人一种“同时”执行的感觉。在Java中多线程的编程有很多方法来实现,这里从Thread类、Runnable与Callable接口以及线程池等几个方式来探讨。

二、Thread类

以下是Thread类的部分源代码:

 public class Thread implements Runnable {

     public Thread() {
//...
} public Thread(Runnable target) {
//...
} public static void sleep(long millis, int nanos)
throws InterruptedException {
//...
} public synchronized void start() {
//...
} public void run() {
//...
}
}

可以看到,Thread类实际上是实现了Runnable接口的,重写了Runnable接口中的run()方法;Thread的对象可以通过Runnable的对象来构造,此时如果调用了run()方法,那么这个run()方法是Runnable对象中的run()方法;调用start()方法会启动线程,并将引发调用run()方法,实现并行运行;sleep()使当前的线程暂停一段时间执行,此时当前线程的还占用着资源,并不会释放资源,等到时间到了就会接着继续执行。

一个Thread的简单示例,以帮助理解:

class TestThread extends Thread{
public void run(){
//...
}
}
public class Test{
public static void main(String []args){
Thread t=new TestThread();
t.start();
}
}

三、Runnable接口与Callable接口

还是从Runnable与Callable的源码开始,

public interface Runnable {
public abstract void run();
} public interface Callable<V> {
V call() throws Exception;
}

Runnable封装一个异步运行的任务,可以把它想象成一个没有参数和返回值的异步方法,而run()方法提供所要执行任务的指令。Callable与Runnable类似,不同的是Callable接口是一个参数化的类型,而且有返回值,返回值的类型是参数的类型,如Callable<Integer>是最终返回一个Integer对象的异步任务;Callable提供一个返回参数类型的call方法,和run()的作用类似。

Runnable与Callable接口的应用示例:

/**
* Runnable接口
*
*/
class TestRunnable implements Runnable{
public void run() {
//...
}
}
public class Test {
public static void main(String[] args){
Thread t=new Thread(new TestRunnable());
t.start();
}
} /**
* Callable接口
*
*/
class TestCallable implements Callable<String>{
public String call() throws Exception {
String s="good";
return s;
}
}
public class Test {
public static void main(String[] args) throws Exception{
/**
* FutureTask实现了Future(用于保存异步计算的结果)和Runnable的接口,可将Callable转换成Future和Runnable
*/
FutureTask<String> task=new FutureTask<String>(new TestCallable());
Thread t=new Thread(task);
t.start();
//取得Callable异步线程的返回值
System.out.println(task.get());
}
}

四、线程池

构造一个新的线程是有一定代价的,会涉及到与操作系统的交互。如果程序中创建了大量的生命周期很短的线程,应该使用线程池(thread pool)。一个线程池中包含一些准备运行的空闲线程。将Runnable对象交给线程池,就会有一个线程调用run()方法,当run()方法退出时,线程不会死亡,而是在线程池中准备为下一个请求提供服务。

使用线程池还会减少并发线程的数量。创建大量的线程会大大降低性能甚至是虚拟机崩溃,所有最好是使线程的数目是“固定”的,以限制并发线程的总数。

线程池的创建是使用执行器(Executors)的静态工厂方法,如下:

1、newCachedThreadPool 构建一个线程池,对于每个任务,如果有空闲线程可用,则立即让它执行,如果没有空闲的线程,则创建一个线程,空闲线程会被保留60秒;

2、newFixedThreadpool 构建一个具有固定大小的线程池。如果提交的任务多于空闲的线程数,那么把到不到服务的任务放置到队列中,其它任务完成后再运行它们;

3、newSingleThreadExecutor 是一个退化了大小为1的线程池,由一个线程提交任务,一个接一个。

这三个方法都返回了实现了ExecutorService接口的ThreadPoolExecutor类的对象。使用submit方法将Runnable或Callable对象提交给ExecutorService,在调用了submit时会得到一个Future对象,可用来查询任务的状态,future的接口如下:

public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}

cancel方法用来取消运行的,如果还没开始运行,则它被取消且不再开始;如果正在运行中,则若mayInterrupt参数为true,它就被中断;

isDone判断任务是否完成,若任务在运行中,isDone返回fasle,如果完成了,则返回true;

不带参数的get方法调用时被阻塞,直到计算完成;如果在计算完成之前,第二个get的调用超时,就会抛出异常;如果该线程被中断,那么两个get都会抛出异常。

线程池的执行流程如下:

1、调用Executors类中的静态方法创建线程池;

2、使用submit提交Ruunable或Callable对象;

3、保存返回的Future对象;

4、没有任务提交时,调用shutdown来关闭线程池。

线程池的示例如下:

/**
* 线程池
*
*/
class MyCallable implements Callable<String>{
public String call() throws Exception {
String s="good";
return s;
}
}
public class Test {
public static void main(String[] args) throws Exception{
//创建大小固定为3的线程池
ExecutorService pool=Executors.newFixedThreadPool(3);
//提交一个Callable任务到线程池,并把结果保存到future对象中
Future<String> res=pool.submit(new MyCallable());
//得到线程执行返回的结果
System.out.println(res.get());
//关闭线程池
pool.shutdown();
}
}

Java多线程之Thread、Runnable、Callable及线程池的更多相关文章

  1. JAVA多线程之UncaughtExceptionHandler——处理非正常的线程中止

    JAVA多线程之UncaughtExceptionHandler——处理非正常的线程中止 背景 当单线程的程序发生一个未捕获的异常时我们可以采用try....catch进行异常的捕获,但是在多线程环境 ...

  2. (一)java多线程之Thread

    本人邮箱: kco1989@qq.com 欢迎转载,转载请注明网址 http://blog.csdn.net/tianshi_kco github: https://github.com/kco198 ...

  3. java多线程之:创建开启一个线程的开销

    ---->关于时间,创建线程使用是直接向系统申请资源的,这里调用系统函数进行分配资源的话耗时不好说.---->关于资源,Java线程的线程栈所占用的内存是在Java堆外的,所以是不受jav ...

  4. Java多线程之Runnable与Thread

    Java多线程之Thread与Runnable 一.Thread VS Runnable 在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类和 ...

  5. Java基础-进程与线程之Thread类详解

    Java基础-进程与线程之Thread类详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.进程与线程的区别 简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程 ...

  6. java多线程之wait和notify协作,生产者和消费者

    这篇直接贴代码了 package cn.javaBase.study_thread1; class Source { public static int num = 0; //假设这是馒头的数量 } ...

  7. JAVA多线程之wait/notify

    本文主要学习JAVA多线程中的 wait()方法 与 notify()/notifyAll()方法的用法. ①wait() 与 notify/notifyAll 方法必须在同步代码块中使用 ②wait ...

  8. JAVA多线程之volatile 与 synchronized 的比较

    一,volatile关键字的可见性 要想理解volatile关键字,得先了解下JAVA的内存模型,Java内存模型的抽象示意图如下: 从图中可以看出: ①每个线程都有一个自己的本地内存空间--线程栈空 ...

  9. java多线程之yield,join,wait,sleep的区别

    Java多线程之yield,join,wait,sleep的区别 Java多线程中,经常会遇到yield,join,wait和sleep方法.容易混淆他们的功能及作用.自己仔细研究了下,他们主要的区别 ...

随机推荐

  1. golang echo livereload

    echo on port 1323 gin -a 1323 run server.go go get github.com/codegangsta/gin gin -h

  2. hadoop完全分布式集群的搭建

    集群配置: jdk1.8.0_161 hadoop-2.6.1 linux系统环境:Centos6.5 创建普通用户  dummy 准备三台虚拟机master,slave01,slave02 hado ...

  3. IE下Date.parse出现NaN有关问题解决

    IE不支持"2000-01-01"这种格式的,但是谷歌浏览器支持,改成"2000/01/01"就可以了. 下面的方法两种浏览器就就都支持了 Date.parse ...

  4. 12、jQuery知识总结-2

    1.避免冲突 jQuery 使用 $ 符号作为 jQuery 的简介方式 <html> <head> <script type="text/javascript ...

  5. 56、使用android studio(v1.3.*)修改包名 (rename package name)

    一.修改包名 ①选中目录,开始构造 在弹窗中选中Rename directory 在弹窗中选中Rename package 填写新的包名,点击Refactor 如果有警告,不用管它,直接点击Do Re ...

  6. 使用Fiddler对Android应用进行抓包

    1.  打开Fiddler软件,效果图如下: 2. 首先,确保安装 Fiddler 的电脑和你的手机在同一局域网内,因为Fiddler只是一个代理,需要将手机的代理指向 PC 机,不能互相访问是不行的 ...

  7. 【LeetCode】移除元素(Remove Element)

    这道题是LeetCode里的第27道题. 题目描述: 给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度. 不要使用额外的数组空间,你必须在原 ...

  8. 学习成绩>=90分的同学用A表示,60-89分之间的用B表示,60分以下的用利用条件运算符的嵌套来完成此题:C表示。

    # -*- coding: utf8 -*- # Author:wxq #python 2.7 #题目:学习成绩>=90分的同学用A表示,60-89分之间的用B表示,60分以下的用利用条件运算符 ...

  9. 【homework #1】第一次作业被虐感受

    当大二暑假结束,我发现我还是没有熟练掌握一门编程语言时,我就知道苦日子要来了. 这不,才开学第三周,就已经被虐的体无完肤了.连编译课用C语言写的词法分析,都要写很久.Debug很久才能写出来,更别提大 ...

  10. jdk -version could not open jvm.cfg 的解决办法

    java 时出现 could not open jvm.cfg 的解决办法     问题描述: 重装JDK并更变目录后,出现JAVA -VERSION  出现could not open jvm.cf ...