一、多线程

线程是指进程中的一个执行流程,一个进程中可以有多个线程。如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. A1012 The Best Rank (25)(25 分)

    A1012 The Best Rank (25)(25 分) To evaluate the performance of our first year CS majored students, we ...

  2. DFS:HDU1518-Square(剪枝较多的DFS)

    题目: Square Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Tota ...

  3. python数据模型(特殊方法)

    python中的全部特殊方法 本部分内容可以参考官方网址 python中一共有83个特殊方法,其中47个用于算术运算.位运算和比较操作.我根据<流畅的python>中的整理,摘录如下两个表 ...

  4. wcf第三方客户端与wcf服务之间调用入门

    Wcf服务与我们的客户端如何建立联系的呢.本文简单记录一下 1.创建我们的wcf服务程序. 第一个wcf服务库是创建我们的wcf库,运行时会单独来托管我们的程序,而非托管在iis下. 第二个wcf服务 ...

  5. Android 完美解决bundle实现页面跳转并保留之前数据+传值

    1.前言 前言: 昨天碰到了一个问题,我想实现页面跳转,采用了Bundle之后,再回到原来的页面,发现数据也没有了, 而且一直报错,网上查找了很多资料,发现要用一个startActivityForRe ...

  6. 11,scrapy框架持久化存储

    今日总结 基于终端指令的持久化存储 基于管道的持久化存储 今日详情 1.基于终端指令的持久化存储 保证爬虫文件的parse方法中有可迭代类型对象(通常为列表or字典)的返回,该返回值可以通过终端指令的 ...

  7. np.newaxis()用法

    这个是liaspace函数 这个是np.newaxis的用法,增加维度,写一个表示增加一维,两个表示增加2维2位置的:号是对a的取值范围,如果把np.newaxis作为第一个参数是对行增加维度,作为第 ...

  8. laravel5.2总结--csrf保护

      CSRF攻击:     CSRF 顾名思义,是伪造请求,冒充用户在站内的正常操作.我们知道,绝大多数网站是通过 cookie 等方式辨识用户身份(包括使用服务器端 Session 的网站,因为 S ...

  9. 将FragmentManger事务添加到返回栈中

    FragmentManger事务添加或替换的 Fragment 后,这时点击 Back 键,程序并不会返回添加之前的状态. 我们可以使用 Transaction 对象的 addToBackStack( ...

  10. Python+Selenium练习篇之1-摘取网页上全部邮箱

    前面已经介绍了Python+Selenium基础篇,通过前面几篇文章的介绍和练习,Selenium+Python的webUI自动化测试算是入门了.接下来,我计划写第二个系列:练习篇,通过一些练习,了解 ...