1.线程简介
线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。
一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程
一个进程可以产生多个线程。同多个进程可以共享操作系统的某些资源一样,同一进程的多个线程也可以共享此进程的某些资源(比如:代码、数据),所以线程又被称为轻量级进程(lightweight process)。
1. 一个进程内部的一个执行单元,它是程序中的一个单一的顺序控制流程。
2. 一个进程可拥有多个并行的(concurrent)线程。
3. 一个进程中的多个线程共享相同的内存单元/内存地址空间,可以访问相同的变量和对象,而且它们从同一堆中分配对象并进行通信、数据交换和同步操作。
4. 由于线程间的通信是在同一地址空间上进行的,所以不需要额外的通信机制,这就使得通信更简便而且信息传递的速度也更快。
5. 线程的启动、中断、消亡,消耗的资源非常少。
- 程序运行原理
- 分时调度
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。 - 抢占式调度
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
- 分时调度
其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高
2.创建线程方式1
继承Thread类
继承Thread类实现多线程的步骤:
1.自定义线程类继承Thread类
2.重写run()方法, 编写线程执行体
3.创建线程对象, 调用start()方法启动线程
示例:
public class TestThread extends Thread {//自定义类继承Thread类//run()方法里是线程体public void run() {for (int i = 0; i < 10; i++) {System.out.println(this.getName() + ":" + i);//getName()方法是返回线程名称}}public static void main(String[] args) {TestThread thread1 = new TestThread();//创建线程对象thread1.start();//启动线程TestThread thread2 = new TestThread();thread2.start();}}
不推荐使用:**避免OOP单继承局限性
3.创建线程方式2
实现Runnable接口
实现Runnable接口i多线程的步骤:
1.自定义线程类实现Runnable接口
2.重写run()方法, 编写线程执行体
3.通过静态代理将实现了Runnabled的子类, 作为参数传递给Thread类的构造函数
4.调用Thread类的start()方法开启线程
示例:
public class TestThread2 implements Runnable {//自定义类实现Runnable接口;//run()方法里是线程体;public void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName() + ":" + i);}}public static void main(String[] args) {//创建线程对象,把实现了Runnable接口的对象作为参数传入;Thread thread1 = new Thread(new TestThread2());thread1.start();//启动线程;Thread thread2 = new Thread(new TestThread2());thread2.start();}}
推荐使用: **避免单继承局限性,灵活方便, 方面同一个对象被多个线程使用**
4.创建线程方式3
实现Callable接口
使用Runnable接口实现的多线程可以避免单继承局限,但是他不能返回结果.
为了解决这个,提供了一个新的接口java.util.concurrent.Callable
@FunctionalInterfacepublic interface Callable<V> {V call() throws Exception;}
call()方法执行完线程的主题功能之后可以返回一个结果,而返回结果的类型由Callable的泛型决定
步骤
实现Callable接口
Thread类里面没有发现并没有直接支持Callable接口的多线程应用
从jdk1.5开始提供有java.util.concurrent.FutureTask<V>类,这个类主要负责Callable
public class FutureTask<V> extends Object implement RunnableFuture<V>
发现他实现了RunnableFuture
public interface RunnableFuture<V> extends Runnable, Future<V>
然后我们看,在FutureTask类里面定义有如下结构的构造方法:
public FutureTask(Callable<V> callable)
接收的目的只有一个,那么取得call()方法的返回结果
FutureTask是Runnable接口的子类,所以可以使用Thread类的构造来接收task对象
多线程执行完毕可以取得内容,依靠FutureTask的父接口Future中的get()方法完成
示例:
package com.study;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
class MyThread implements Callable<String> {
private int ticket = 10;
@Override
public String call() throws Exception {
for (int i = 0; i < 100; i++) {
if (this.ticket > 0) {
System.out.println("卖票--- " + this.ticket--);
}
}
return "卖光了";
}
}
public class HelloWorld {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread m1 = new MyThread();
MyThread m2 = new MyThread();
//目的为了取得线程的返回值,String是泛型,对应返回类型
FutureTask<String> task1 = new FutureTask<>(m1);
FutureTask<String> task2 = new FutureTask<>(m2);
//FutureTask是Runnable接口的子类,所以可以使用Thread类的构造来接收task对象
//启动多线程
new Thread(task1).start();
new Thread(task2).start();
//多线程执行完毕可以取得内容,依靠FutureTask的父接口Future中的get()方法完成
System.out.println("A线程返回结果: "+ task1.get());
System.out.println("B线程返回结果: "+ task2.get());
}
}
5.简化线程写法
1.使用匿名内部类
2.在匿名内部类的基础上推导lambda表达式简化代码
使用线程的内匿名内部类方式,可以方便的实现每个线程执行不同的线程任务操作。
- 方式1:创建线程对象时,直接重写Thread类中的run方法
```java
//1.匿名内部类的使用
new Thread() {
public void run() {
} }.start();for (int x = 0; x < 40; x++) { System.out.println(Thread.currentThread().getName() + "...X...." + x); }
//2.jdk8 lambda new Thread(() ->{ for (int x = 0; x < 40; x++) { System.out.println(Thread.currentThread().getName()
+ "...X...." + x); }
}).start();
- 方式2:使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法
```java
//1.匿名内部类的使用
Runnable r = new Runnable() {
public void run() {
for (int x = 0; x < 40; x++) {
System.out.println(Thread.currentThread().getName()
+ "...Y...." + x);
}
}
};
new Thread(r).start();
//2.jdk8 lambda
new Thread(() ->{
for (int x = 0; x < 40; x++) {
System.out.println(Thread.currentThread().getName()
+ "...Y...." + x);
}
}).start();
