线程的内存模型
进程A 和 进程B 的内存独立不共享。
线程A 和 线程B 在Java中,堆内存和方法区内存共享。但是栈内存独立,一个线程一个栈。
继承Thread类,重写run方法
class StartThread01 extends Thread {
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("我在学习i=" + i);
}
}
}
public static void main(String[] args) throws InterruptedException {
StartThread01 p = new StartThread01();
p.start();
System.out.println(Thread.currentThread().getName());
Thread.sleep(100);
for (int i = 0; i < 100; i++) {
System.out.println("主线程调用i=======" + i);
}
}
实现Runnable接口,重写run方法
new Thread(new MyRunnable).start()
底层就是静态代理的原理,Thread是代理类,代理目标是Runnable接口的实现类。start是代理类去调用的,Runnable接口的实现类并不包含start方法。
将Runnable实现类,传入Thread类的构造方法中。
// 这只是一个可运行的类(Runnable),还不是一个线程
class MyRunnable implements Runnable {
public void run() {}
}
// 创建线程对象
Thread t = new Thread(new MyRunnable);
// 启动线程
t.start();
// 采用匿名内部类的形式
Thread t = new Thread(new Runnable(){
public void run() {
}
});
实现Callable接口,实现call方法
可以返回运行结果或抛出异常FutureTask
其实也是实现的Runnable接口
FutureTask(Callable[**V**](https://www.matools.com/file/manual/jdk_api_1.8_google/java/util/concurrent/FutureTask.html) callable) 创建一个 FutureTask ,它将在运行时执行给定的 Callable 。 |
---|
FutureTask(Runnable runnable, V result) 创建一个 FutureTask ,将在运行时执行给定的 Runnable ,并安排 get将在成功完成后返回给定的结果。 |
可以传Callable 或者 Runable ,传入Runnable将没有返回值
方法
V | get() 等待计算完成,然后检索其结果。 |
---|---|
会导致当前线程阻塞,因为必须等待其他线程运行结束得到运行结果。
- 此时使用FutureTask类接收Callable接口,然后将FutureTask类传入Thread类,再通过Thread类启动线程。
- 使用线程池的submit接收Callable实现类,同时自动启动,然后通过Future<>接收结果 ```java import java.util.concurrent.FutureTask; // jdk8新增的包
public static void main(String[] args) {
// 第一步:创建一个未来任务类对象(此处使用匿名类)
FutureTask task = new FutureTask(new Callable<>(){
// call() 相当于之前的run方法,只是此处有返回值
public Object call() throws Exception {
...
return new Object();
}
});
// 更改--使用线程池
ExecutorService executor = Executors.newCachedThreadPool();
executor.submit(task);
System.out.println(task.get());
//
}
```java
public class ThreadBase03 implements Callable<Boolean> {
@Override
public Boolean call() throws Exception {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " 执行:" + i);
}
return true;
}
public static void main(String[] args) throws Exception {
ThreadBase03 thread01 = new ThreadBase03();
ThreadBase03 thread02 = new ThreadBase03();
ThreadBase03 thread03 = new ThreadBase03();
// 创建执行服务
ExecutorService executorService = Executors.newFixedThreadPool(3);
Future<Boolean> r1 = executorService.submit(thread01);
Future<Boolean> r2 = executorService.submit(thread02);
Boolean r3 = executorService.submit(thread03).get();
Boolean rs1 = r1.get();
Boolean rs2 = r2.get();
// Boolean rs3 = r3.get();
// 必须关闭线程池
executorService.shutdown();
}
}
第一个例子和第二个例子的区别是,第一个例子调用submit方法没有返回值,实际上调用的是submit(Runnable task)
方法,而第二个例子调用的是submit(Callable<T> task)
方法。而且第一个例子使用FutureTask去取值,第二个例子使用submit返回的future去取值。
在很多高并发的环境下,有可能Callable和FutureTask会创建多次。FutureTask能够在高并发环境下确保任务只执行一次。有兴趣的可以参看FutureTask源码。