1. 进程 vs 线程
- 进程:正在运行的程序,是资源分配的最小单位(基本单位)
- 线程是CPU调度的最小单位
- 轻量级进程
- 进程中的一条执行路径,是 CPU 的基本调度单位
- 一个进程由一个或多个线程组成,彼此间完成不同的工作,同时执行,称为多线程
- 一台设备能并行执行多少个线程取决于CPU核心数
例如:Java 虚拟机是一个进程,当中默认包含主线程(main),可通过代码创建多个独立线程,与 main 并发执行
1.1 线程的特点
- 线程抢占式执行
- 在单核 CPU 中,宏观上同时执行,微观上顺序执行
1.2 并发与并行
- 并发指一个 CPU 同时处理多个任务(轮流使用 CPU 时间片)
- 并行是指多个 CPU 或多核 CPU 同时处理多个不同的任务
2. 创建线程
- 在 JDK 中代表线程的就只有 Thread 类
- 线程的执行单元是 run(),可以通过继承 Thread 然后重写 run() 实现,也可以实现 Runnable 接口
准确地讲,创建线程只有一种方式那就是构造 Thread 类,而实现线程的执行单元则有两种方式,第一种是重写 Thread 的 run(), 第二种是实现 Runnable 接口的 run(), 并且将 Runnable 实例用作构造 Thread 的参数
2.1 示例一:继承 Thread
public class MyThread extends Thread {
@Override
public void run() {
// // this.getId() 和 this.getName() 有局限性,必须是 extends Thread 才能使用,因为这是父类中的方法
System.out.println("线程id: " + this.getId() + "; 线程name: " + this.getName() + "; Thread name: " + Thread.currentThread().getName());
}
}
MyThread thread = new MyThread();
thread.start();
匿名内部类方式:
- 子类重写父类的 run()
- 语法
new 父类() { 子类内容 };
匿名类是一个表达式,因此在定义的最后用分号;
结束
new Thread() {
@Override
public void run(){
System.out.println("threa start...");
}
}.start();
其他方式:将线程对象作为构造参数
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();
}
2.2 示例二:实现 Runnable
MyThread myThread = new MyThread();
// 创建线程对象
Thread thread = new Thread(myThread, "Thread name");
thread.start();
public class MyThread implements Runnable {
@Override
public void run() {
System.out.println("run...");
}
}
匿名内部类方式
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override
public void run() { // ... }
};
Thread thread = new Thread(runnable);
Thread thread2= new Thread(runnable, "t2");
thread.start();
thread2.start();
}
2.3 示例三:实现 Callable(Since: 1.5)
public static void main(String[] args) throws Exception {
Callable<Integer> callable = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i <= 100; i++) {
sum += i;
}
return sum;
}
};
// Callable 有返回值,需要 FutureTask 来接收执行结果
FutureTask<Integer> task = new FutureTask<>(callable);
new Thread(task).start();
// 接收运行的结果
// get() 是阻塞的,等 call() 执行完才会返回
Integer result = task.get();
System.out.println(result);
}
3. Runable / Callable / Future
3.1 Runable vs Callable
- Runnable 的 run() 无返回值
- Callable 的 call() 有返回值,并且有声明异常
public interface Runnable {
public abstract void run();
}
public interface Callable<V> {
V call() throws Exception;
}
3.2 Future 接口
- Future 表示将要完成任务的结果
public interface Future<V> {
// get() 以阻塞形式等待 Future 中的异步处理结果
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}
public class FutureTask<V> implements RunnableFuture<V> {
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
}
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
4. 其他
4.1 设置线程名称
直接 setName() 或者 在构造方法里设置
// 方式一:直接 setName()
MyThread thread = new MyThread();
thread.start();
thread.setName("Thread name");
// 方式二:使用构造方法
public class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
}
4.2 将 Callable 放入线程池执行
public class Demo {
public void test() throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(3);
Callable<Integer> caller = new Caller(10);
Future<Integer> future = executorService.submit(caller);
System.out.println(future.get());
executorService.shutdown();
}
class Caller implements Callable<Integer> {
private Integer sum = 0;
public Caller(Integer sum) {
this.sum = sum;
}
@Override
public Integer call() throws Exception {
for (int i = 0; i <= 100; i++) {
sum += i;
}
return sum;
}
}
}
网友评论