Java并发编程系列文章
《一》多线程基础——Java线程与进程的基本概念
《二》多线程基础——Java线程入门类和接口
《三》多线程基础——Java线程组和线程优先级
《四》多线程基础——Java线程生命周期及转换
《五》多线程基础——Java线程间的通信(互斥与协作)
《六》实际应用——如何优雅的关闭线程
《七》实际应用——生产者与消费者模型
并发编程(多线程)
一直以来都是程序员头疼的难题。曾经听别人总结过并发编程的第一原则,那就是不要写并发程序,哈哈哈。后来发现,这样能够显著提高程序响应和吞吐量的利器,哪还能忍得住不会用呢?
整理出《Java并发编程系列文章》,共计7篇,与君共勉。
《二》多线程基础——Java线程入门类和接口
1、Thread
JDK提供了Thread
类来让我们实现⾃⼰的“线程”类:继承 Thread 类,并重写 run ⽅法。
public class Demo {
public static class MyThread extends Thread {
@Override
public void run() {
System.out.println("MyThread");
}
}
public static void main(String[] args) {
Thread myThread = new MyThread();
myThread.start();
}
}
调⽤start()
后,虚拟机会先为我们创建⼀个线程,然后等到这个线程第⼀次得到时间片时再调⽤run()
⽅法。多次调⽤start()
⽅法,程序会抛出异常。
Thread类的⼏个常⽤的⽅法:
currentThread():静态⽅法,返回对当前正在执⾏的线程对象的引⽤;
start():开始执⾏线程的⽅法,java虚拟机会调⽤线程内的run()⽅法;
yield():yield在英语⾥有放弃的意思,同样,这⾥的yield()指的是当前线程愿
意让出对当前处理器的占⽤。这⾥需要注意的是,就算当前线程调⽤了yield() ⽅法,程序在调度的时候,也还有可能继续运⾏这个线程的;
sleep():静态⽅法,使当前线程睡眠⼀段时间;
join():使当前线程等待另⼀个线程执⾏完毕之后再继续执⾏,内部调⽤的是
Object类的wait⽅法实现的;
2、Runnable
JDK提供了 Runnalble
接⼝来让我们实现⾃⼰的“线程”类:实现 Runnable 接⼝的 run ⽅法。
public class Demo {
public static class MyThread implements Runnable {
@Override
public void run() {
System.out.println("MyThread");
}
}
public static void main(String[] args) {
new Thread(new MyThread()).start();
}
}
Runnable
只是一个函数式接口,源码如下。Thread 类
是⼀个 Runnable 接⼝
的实现类,源码如下。
它们是策略模式的一个经典应用。什么是策略模式?看这里。在这个策略模式中,Runnable 接⼝
作为策略接口,Thread 类
作为逻辑类。Thread
中添加一个成员变量target
用于保存对于Runnable
实现类对象的引用。 然后通过构造方法以修改该成员变量。 通过传入不同的Runnable
实现类对象,即可将控制逻辑,与具体任务单元抽离开。
/**
Runnable源码
*/
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
/**
Thread源码片段
*/
public class Thread implements Runnable {
/* Make sure registerNatives is the first thing <clinit> does. */
private static native void registerNatives();
static {
registerNatives();
}
private volatile String name;
private int priority;
private Thread threadQ;
private long eetop;
/* Whether or not to single_step this thread. */
private boolean single_step;
/* Whether or not the thread is a daemon thread. */
private boolean daemon = false;
/* JVM state */
private boolean stillborn = false;
/* What will be run. */
private Runnable target;
......
3、Callable、Future与FutureTask
通常来说,我们使⽤ Runnable
和 Thread
来创建⼀个新的线程。但是它们有⼀个弊端,就是 run
⽅法是没有返回值的。⽽有时候我们希望开启⼀个线程去执⾏⼀个任务,并且这个任务执⾏完成后有⼀个返回值。JDK提供了 Callable接⼝
与 Future类
为我们解决这个问题,这也是所谓的异步模型。
Callable
与 Runnable
类似,同样是只有⼀个抽象⽅法的函数式接⼝。不同的是, Callable
提供的⽅法是有返回值的,⽽且⽀持泛型。
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
一般来说,Callable
都是配合线程池工具ExecutorService
一起用的,利用ExecutorService
可以使⽤ submit ()
来让⼀个 Callable 接⼝
执⾏。它会返⼀个 Future
,可以通过这个 Future
的 get ()
得到结果。
这⾥可以看⼀个简单的使⽤demo:
// ⾃定义Callable
class Task implements Callable<Integer>{
@Override
public Integer call() throws Exception {
// 模拟计算需要⼀秒
Thread.sleep(1000);
return 2;
}
public static void main(String args[]){
// 使⽤
ExecutorService executor = Executors.newCachedThreadPool();
Task task = new Task();
Future<Integer> result = executor.submit(task);
// 注意调⽤get⽅法会阻塞当前线程,直到得到结果。
// 所以实际编码中建议使⽤可以设置超时时间的重载get⽅法。
System.out.println(result.get());
}
}
输出:
2