我们先来分别看一下这两个接口

Runnable:

 //
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// package java.lang; @FunctionalInterface
public interface Runnable {
void run();
}

只有一个方法run(),表示执行任务的逻辑。

Callable:

 //
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// package java.util.concurrent; @FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}

也只有一个方法call(),但是是一个有返回值的方法,这给我们提供了获取方法执行结果的可能,即使它是个异步的任务,它可以获取异常,给我们极大地便利知道任务执行失败的原因。

Runnable例子Demo:

 package cn.concurrent.executor;

 /**
* Created by spark on 17-9-24.
*/
public class RunnabelDemo implements Runnable { public RunnabelDemo(String acceptStr) {
this.acceptStr = acceptStr;
} private String acceptStr; @Override
public void run() {
try {
// 线程阻塞 1 秒,此时有异常产生,只能在方法内部消化,无法上抛
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 最终处理结果无法返回
System.out.println("hello : " + this.acceptStr);
} public static void main(String[] args) {
Runnable runnable = new RunnabelDemo("my runable test!");
long beginTime = System.currentTimeMillis();
new Thread(runnable).start();
long endTime = System.currentTimeMillis();
System.out.println("cast : " + (endTime - beginTime) / 1000 + " second!");
}
}

结果:

 /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/java -javaagent:/usr/local/idea/lib/idea_rt.jar=41745:/usr/local/idea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/charsets.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/icedtea-sound.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/jce.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/jsse.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/management-agent.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/resources.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/rt.jar:/home/spark/IdeaProjects/thread/thread/target/classes:/home/spark/.m2/repository/com/lmax/disruptor/3.3.2/disruptor-3.3.2.jar cn.concurrent.executor.RunnabelDemo
cast : 0 second!
hello : my runable test! Process finished with exit code 0

Callable的例子:

 package cn.concurrent.executor;

 import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask; /**
* Created by spark on 17-9-24.
*/
public class CallableDemo implements Callable<String> { private String name; public CallableDemo(String name) {
this.name = name;
} @Override
public String call() throws Exception {
//执行任务的相关逻辑
Thread.sleep(1000);
return name + " : this is task is successed.";
} public static void main(String[] args) throws ExecutionException, InterruptedException {
CallableDemo callableDemo = new CallableDemo("wade");
//异步执行的结果
FutureTask<String> future = new FutureTask<String>(callableDemo);
new Thread(future).start();
//調用get()方法--阻塞
String result=future.get();
System.out.println("hello : " + result);
}
}

结果:

 /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/java -javaagent:/usr/local/idea/lib/idea_rt.jar=37879:/usr/local/idea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/charsets.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/icedtea-sound.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/jce.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/jsse.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/management-agent.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/resources.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/rt.jar:/home/spark/IdeaProjects/thread/thread/target/classes:/home/spark/.m2/repository/com/lmax/disruptor/3.3.2/disruptor-3.3.2.jar cn.concurrent.executor.CallableDemo
hello : wade : this is task is successed. Process finished with exit code 0

问题:如何把一个Runnbale的任务转变为有返回值的Callable的任务。

^_^我们有工具类Executors可以帮我们实现,如下图:

其中Executors.callable(Runnable),就是我们需要的方法,是不是很简单呢。

代码如下:

 package cn.concurrent.executor;

 import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask; /**
* Created by spark on 17-9-24.
*/
public class RunnableToCallable implements Runnable { String name; public RunnableToCallable(String name) {
this.name = name;
} @Override
public void run() {
try {
Thread.sleep(1000);
System.out.println(name + " :任务执行完毕!");
} catch (InterruptedException e) {
e.printStackTrace();
}
} public static void main(String[] args) throws ExecutionException, InterruptedException {
RunnableToCallable task=new RunnableToCallable("wade");
//转变为有返回值的任务
Callable<Object> future=Executors.callable(task);
FutureTask<Object> futureTask=new FutureTask<Object>(future);
new Thread(futureTask).start();
//調用get()方法--阻塞
Object result=futureTask.get();
System.out.println("hello : " + result);
}
}

结果如下:

 /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/java -javaagent:/usr/local/idea/lib/idea_rt.jar=36136:/usr/local/idea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/charsets.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/icedtea-sound.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/jce.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/jsse.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/management-agent.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/resources.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/rt.jar:/home/spark/IdeaProjects/thread/thread/target/classes:/home/spark/.m2/repository/com/lmax/disruptor/3.3.2/disruptor-3.3.2.jar cn.concurrent.executor.RunnableToCallable
wade :任务执行完毕!
hello : null Process finished with exit code 0

说明:因为该任务没有返回结果,因此返回的为null.

接下来我们讨论一下ExecutorService.executr()与ExecutorService.submit()方法的区别。

类图如下:

从类图可以看到:

execute只能接受Runnable类型的任务,并且没有返回值。

submit不管是Runnable还是Callable类型的任务都可以接受,但是Runnable返回值均为void,所以使用Future的get()获得的还是null

然后来看异常处理:

对于Runnable 只能通过try-catch来捕获异常,对于Callable,直接抛出就可以,然后在get的时候捕获异常进行处理。

如下:

 package cn.concurrent.executor;

 import java.util.concurrent.*;

 /**
* Created by spark on 17-9-24.
*/
public class ThreadExceptionTest { public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
CallableTest callableTest = new CallableTest();
Future<Boolean> task = executorService.submit(callableTest);
try {
Boolean b = task.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} } static class CallableTest implements Callable<Boolean> { @Override
public Boolean call() throws Exception {
int num = 3 / 0;
return false;
}
}
}

结果:

 /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/java -javaagent:/usr/local/idea/lib/idea_rt.jar=42960:/usr/local/idea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/charsets.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/icedtea-sound.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/jce.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/jsse.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/management-agent.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/resources.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/rt.jar:/home/spark/IdeaProjects/thread/thread/target/classes:/home/spark/.m2/repository/com/lmax/disruptor/3.3.2/disruptor-3.3.2.jar cn.concurrent.executor.ThreadExceptionTest
java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at cn.concurrent.executor.ThreadExceptionTest.main(ThreadExceptionTest.java:16)
Caused by: java.lang.ArithmeticException: / by zero
at cn.concurrent.executor.ThreadExceptionTest$CallableTest.call(ThreadExceptionTest.java:29)
at cn.concurrent.executor.ThreadExceptionTest$CallableTest.call(ThreadExceptionTest.java:25)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748) Process finished with exit code 0

get()方法会捕获异常。

接下来,我们详细了解一下Future接口,实现类FutureTask(它有三种状态,分别为未启动,已启动,已完成);

类图如下:

1.首先可以看到,它也是一个线程,可以直接继承它来运行任务,然后交给ExecutorService执行。

如下:

 package cn.concurrent.executor;

 import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask; /**
* Created by spark on 17-9-24.
*/
public class FutureTaskDemo extends FutureTask<String> {
public FutureTaskDemo(Runnable runnable, String s) {
super(runnable, s);
System.out.println(s);
} @Override
public void run() {
try {
Thread.sleep(1000);
System.out.println("this is running.");
} catch (InterruptedException e) {
e.printStackTrace();
}
} public static void main(String[] args) {
FutureTaskDemo futureTaskDemo = new FutureTaskDemo(new Runnable() {
@Override
public void run() {
System.out.println("this is wade.");
}
}, "wade"); ExecutorService es = Executors.newFixedThreadPool(1);
FutureTask<String> task = (FutureTask<String>) es.submit(futureTaskDemo);
System.out.println("hello:"+task);
es.shutdown();
}
}

结果如下:

 /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/java -javaagent:/usr/local/idea/lib/idea_rt.jar=46018:/usr/local/idea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/charsets.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/icedtea-sound.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/jce.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/jsse.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/management-agent.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/resources.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/rt.jar:/home/spark/IdeaProjects/thread/thread/target/classes:/home/spark/.m2/repository/com/lmax/disruptor/3.3.2/disruptor-3.3.2.jar cn.concurrent.executor.FutureTaskDemo
wade
hello:java.util.concurrent.FutureTask@266474c2
this is running. Process finished with exit code 0

2.看一下run()方法

     public void run() {
/**
*首先判断任务的状态 如果任务的状态不是new 说明任务的状态已经改变(说明他已经走了4种可能变化的一种)
* 如果状态是new就会把 当前执行任务的线程付给runner, 这里用的cmpandset如果runner不为空 说明已经有线程在执行
* 任务也会退出执行,如果状态是new并且runner为空并且把当前的线程付给了runner那么就继续执行任务(runner state 都是 volatile
*类型的变量是一个很轻量机的线程安全操作)
*引起state状态变化的原因 就是调用了cancel 或是 run
**/
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return; //开始执行任务
try {
Callable<V> c = callable;
/**
* 如果 要执行的任务不为空 并且状态 new 就执行 ***/
if (c != null && state == NEW) {
V result;
boolean ran;
try {
//执行任务
result = c.call();
//如果没有意外发生就执行成功了
ran = true;
} catch (Throwable ex) {
//有异常
result = null;
ran = false;
//设置异常
setException(ex);
}
//如果转型成功了 设置结果
if (ran)
(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
//不管是否执行成功了 都把runner设置成null
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}

Task执行后如果成功会调用set()方法,如果有异常会调用setException()方法。

我们先看下set方法 :

     protected void set(V v) {
/**
*如过state是new 把state设置成 COMPLETING
*
**/ if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
//将任务设置成NORMAL over the task
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}

最后,来看一下最重要的方法get();

public V get() throws InterruptedException, ExecutionException {
int s = state;
     //当线程状态为新建活着执行中时一直调用awaitDone方法
if (s <= COMPLETING)
       //循环判断线程状态是否已经执行成功,如果执行成功返回线程状态;其中还包括线程取消,中断等情况的判断。可参见下方源码。
       //所以这里便是上面例子中为什么线程执行成功后即可立即得到结果,如果还没有执行成功
s = awaitDone(false, 0L);
       //线程状态正常返回结果
return report(s);
}

如果get时,FutureTask的状态为未完成状态,则调用awaitDone方法进行阻塞。awaitDone():

 private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        //计算一下需要等待的时间,有可能为0,为0的话就无限期等待。
        final long deadline = timed ? System.nanoTime() + nanos : 0L;
        //一个等待节点
        WaitNode q = null;
        //是否加入队列
        boolean queued = false;
        for (;;) {
            //如果当且调用get方法的线程被interrupt 那么就把当前线程从等待队列remove
            //然后抛出异常
            if (Thread.interrupted()) {
                removeWaiter(q);
                throw new InterruptedException();
            }
            int s = state;
            //如果任务已经完成 不管是被暂停了 还是出现异常了 只要状态大于COMPLETING就返回。
            if (s > COMPLETING) {
                if (q != null)
                    q.thread = null;
                return s;
            }
            else if (s == COMPLETING) // cannot time out yet
            //如果任务正在完成中(注意是ING进行时) 那么让出一会CPU时间在继续执行
                Thread.yield();
            else if (q == null)
                //创建等待节点
                q = new WaitNode();
            else if (!queued)
                //第一次创建的等待节点需要加入等待队列,这里加入队列等待。
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                     q.next = waiters, q);
            else if (timed) {
                //如果设置了等待 那么就得锁住线程等待,如果时间到了就返回状态。
                //方法 public V get(long timeout, TimeUnit unit) 这里会根据状态做后续处理。
                nanos = deadline - System.nanoTime();
                if (nanos <= 0L) {
                    removeWaiter(q);
                    return state;
                }
                LockSupport.parkNanos(this, nanos);
            }
            else
                //否则锁住线程等待其他线程解锁。
                LockSupport.park(this);
        }
    }

awaitDone方法可以看成是不断轮询查看FutureTask的状态。在get阻塞期间:

  • 如果执行get的线程被中断,则移除FutureTask的所有阻塞队列中的线程(waiters),并抛出中断异常;

  • 如果FutureTask的状态转换为完成状态(正常完成或取消),则返回完成状态;

  • 如果FutureTask的状态变为COMPLETING, 则说明正在set结果,此时让线程等一等;

  • 如果FutureTask的状态为初始态NEW,则将当前线程加入到FutureTask的阻塞线程中去;

  • 如果get方法没有设置超时时间,则阻塞当前调用get线程;如果设置了超时时间,则判断是否达到超时时间,如果到达,则移除FutureTask的所有阻塞列队中的线程,并返回此时FutureTask的状态,如果未到达时间,则在剩下的时间内继续阻塞当前线程。

详细了解这几个方法:

当FutureTask处于未启动或者已启动状态时,执行Future.get()方法,将导致调用线程阻塞,当FutureTask处于已完成状态时,执行Future.get()方法时,将调用线程立即返回结果或者抛出异常。

当FutureTask处于未启动状态时,执行Future.cancel()方法,导致此任务永远不会被执行 ;当FutureTask处于已启动状态时,执行Future.cancel()方法将以中断执行此任务线程的方式来试图停止任务;当FutureTask处于已启动状态时,执行Future.cancel(false)方法不会对正在执行此任务的线程产生影响。

说明:Future.get()方法的实现基于AbstractQueuedSynchronizer(简称为AQS),并发包中有很多可阻塞类(如ReentrantLock)都是基于AQS实现的,AQS是一个同步框架,它提供了通用机制来原子性管理同步状态,阻塞和唤醒线程,以及维护被阻塞线程的队列,后面有专门博客介绍AQS.

博客有很多的不足,望大家指点。

Java多线程Runnable与Callable区别与拓展的更多相关文章

  1. java中Runnable和Callable的区别

    文章目录 运行机制 返回值的不同 Exception处理 java中Runnable和Callable的区别 在java的多线程开发中Runnable一直以来都是多线程的核心,而Callable是ja ...

  2. -1-5 java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait(),notify(),notifyAll()等方法都定义在Object类中

     本文关键词: java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁  sleep()和wait()方法的区别 为什么wait( ...

  3. java多线程系列(七)---Callable、Future和FutureTask

    Callable.Future和FutureTask 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量 ...

  4. java多线程—Runnable、Thread、Callable区别

    多线程编程优点 进程之间不能共享内存,但线程之间共享内存非常容易. 系统创建线程所分配的资源相对创建进程而言,代价非常小. Java中实现多线程有3种方法: 继承Thread类 实现Runnable接 ...

  5. Java线程—-Runnable和Callable的区别和联系

    Java 提供了三种创建线程的方法 1.继承Thread接口 public class Thread2Thread { public static void main(String[] args) { ...

  6. Java多线程编程:Callable、Future和FutureTask浅析(多线程编程之四)

    java多线程-概念&创建启动&中断&守护线程&优先级&线程状态(多线程编程之一)java多线程同步以及线程间通信详解&消费者生产者模式&死锁& ...

  7. Java多线程runnable

    主要为大家分享Java多线程怎么实现Runnable方式 一 :主要步骤 1.定义实现Runnable接口 2.覆盖Runnable接口中run方法,将线程要运行的代码存在run方法里 3.用Thre ...

  8. Java多线程编程:Callable、Future和FutureTask浅析

    通过前面几篇的学习,我们知道创建线程的方式有两种,一种是实现Runnable接口,另一种是继承Thread,但是这两种方式都有个缺点,那就是在任务执行完成之后无法获取返回结果,那如果我们想要获取返回结 ...

  9. JAVA多线程提高七:Callable与Future的应用

    Callable与Runnable 先说一下java.lang.Runnable吧,它是一个接口,在它里面只声明了一个run()方法: public interface Runnable { publ ...

随机推荐

  1. Composer简介及使用实例

    1.PHP-FIG 官网:http://www.php-fig.org/ php编码规范: 本文档是PHP互操作性框架制定小组(PHP-FIG :PHP Framework Interoperabil ...

  2. 微信小程序结合后台数据管理实现商品数据的动态展示、维护

    微信小程序给我们提供了一个很好的开发平台,可以用于展现各种数据和实现丰富的功能,本篇随笔介绍微信小程序结合后台数据管理实现商品数据的动态展示.维护,介绍如何实现商品数据在后台管理系统中的维护管理,并通 ...

  3. 【.net 深呼吸】实时获取计算结果

    上次老周介绍了在 UWP 应用中通过 x:Bind 标记来绑定到方法,以实现实时获取计算结果.今天,咱们来耍耍WPF上面的实现方法. 虽然,WPF 没有 x:Bind 标记(暂时没有,以后不好说),但 ...

  4. mongodb菜鸟整理 2 C#Driver使用

    一下载 从官网上下载... 二 引用 下载完了将其解压到某个文件夹内,打开vs建立一个工程 右键引用,找到刚才解压的目录,把里面说有的dll文件全部添加就好 三 命名空间 我都是先打然后alt+shi ...

  5. 使用idea和studio进行调试的方法

    新入职一个公司,使用得IDE发生了一些变化 ,对于idea的使用,之前有提到过,今天主要的内容是使用idea和studio进行调试的快捷键. 虽然现在计算机开发的语言多种多样,但是使用C#写客户端,使 ...

  6. BZOJ 3379: [Usaco2004 Open]Turning in Homework 交作业

    Description     贝茜有C(1≤C≤1000)门科目的作业要上交,之后她要去坐巴士和奶牛同学回家. 每门科目的老师所在的教室排列在一条长为H(1≤H≤1000)的走廊上,他们只在课后接收 ...

  7. Vuejs 页面的区域化与组件封装

    组件的好处 当我用vue写页面的时候,大量的数据页面渲染,引入组件简化主页面的代码量,当代码区域块代码差不多相同时,组件封装会更加简化代码.组件是Vue.js最强大的功能之一. 组件可以扩展HTML元 ...

  8. Linux的学习笔记_Day1

    为什么要开始学习Linux命令? 首先当然是因为工作需要了,现在的工作是负责银行调度的系统的源系统接入的工作,经常要到生产部署版本.所以--买了一本<Linux命令行与shell脚本编程大全&g ...

  9. Jenkins关于tomcat地址和端口映射的配置

    <?xml version='1.0' encoding='utf-8'?><!-- Licensed to the Apache Software Foundation (ASF) ...

  10. 详解 mpls vpn 的实现

    MPLS VPN的实现 一.实验目的 该实验通过MPLS VPN的数据配置,使学生掌握路由器相关接口的IP地址设置.路由协议的配置以及MPLS VPN的完整的创建过程, 从而加深对IP网络的IP编址. ...