该学习笔记是根据阿玮老师《黑马程序员Java零基础视频教程》总结出来的,非常感谢阿玮老师。
一. 什么是线程
- 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。进程是程序的基本执行实体(可以理解为一个软件运行就是一个进程,例如运行微信是一个进程,运行QQ也是一个进程,运行浏览器也是一个进程)。进程中有了线程之后,可以同时运行多个功能。
举个例子:小王是一个伟大的工人(唉,其实就是拧螺丝的),在流水线上负责搬货物,一个流水线上每10分钟出现一个货物,也就是小王在一条流水线上每10分钟搬一次货物。但是他老板嫌弃兰博基尼来的慢,所以就让小王负责6条流水线上的货物,这6条流水线上的货物在10分钟之内都出现。也就是说小王原先10分钟之内只需要搬1次货物,现在小王10分钟之内需要搬6次货物,那么我们就可以把流水线看成是线程,6条流水线相当于6个线程,而小王搬货物这件事就可以看成是一个进程。多线程可以看成小王在10分钟之内搬多个货物,也就是在一个时间内干多件事情。
- 并发和并行
多个线程抢夺CPU是随机的,也就是说该线程随时能抢到CPU资源,随时能失去CPU资源。
二. 多线程的实现方式
- 继承Thread类的方式进行实现
public class Test {
public static void main(String[] args) throws IOException {
// 多线程的第一种启动方式
// 1.自己定义一个类继承Thread; 2.重写run方法; 3.创建子类的对象,并启动线程
// 创建子类对象
MyThread mt1 = new MyThread();
mt1.setName("线程1"); // 给线程起名字
MyThread mt2 = new MyThread();
mt2.setName("线程2");
// 启动线程
mt1.start();
mt2.start();
}
}
class MyThread extends Thread{
@Override
public void run() {
//书写线程要执行的代码
for (int i = 0; i < 6; i++) {
System.out.println(getName()+"HelloWorld");
}
}
}
/*输出:
线程1HelloWorld
线程2HelloWorld
线程1HelloWorld
线程2HelloWorld
线程2HelloWorld
线程2HelloWorld
线程2HelloWorld
线程1HelloWorld
线程2HelloWorld
线程1HelloWorld
线程1HelloWorld
线程1HelloWorld
*/
- 实现Runnable接口的方式进行实现
public class Test {
public static void main(String[] args) throws IOException {
// 多线程的第二种启动方式
// 1.自己定义一个类实现Runnable接口; 2.重写run方法; 3.创建自己类的对象; 4.创建一个Thread类对象,并开启线程
// 创建MyRunnable对象,表示多线程要执行的任务
MyRunnable mt = new MyRunnable();
// 创建线程对象
Thread t1 = new Thread(mt);
Thread t2 = new Thread(mt);
//给线程设置名字
t1.setName("线程1");
t2.setName("线程2");
// 开启线程
t1.start();
t2.start();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
//书写线程要执行的代码
for (int i = 0; i < 6; i++) {
// 获取当前线程的对象
Thread t = Thread.currentThread();
System.out.println(t.getName()+"HelloWorld");
}
}
}
/*输出:
线程2HelloWorld
线程1HelloWorld
线程2HelloWorld
线程1HelloWorld
线程2HelloWorld
线程2HelloWorld
线程2HelloWorld
线程2HelloWorld
线程1HelloWorld
线程1HelloWorld
线程1HelloWorld
线程1HelloWorld
*/
- 利用Callable接口和Future接口的方式进行实现
public class Test {
public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {
// 多线程的第三种实现方式
// 1.自己定义一个类MyCallable实现Callable接口; 2.重写call方法(有返回值,表示多线程运行的结果);
// 3.创建MyCallable类的对象(表示多线程要执行的任务); 4.创建FutureTask的对象(作用是管理多线程运行的结果)
// 5.创建Thread对象,并启动(表示线程)
// 创建MyCallable类的对象
MyCallable mc = new MyCallable();
//创建FutureTask的对象
FutureTask<Integer> ft = new FutureTask<>(mc);
// 创建Thread对象
Thread t1 = new Thread(ft);
// 启动线程
t1.start();
int sum = ft.get();
System.out.println(sum);
}
}
class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
//计算前100个数的和
int sum = 0;
for (int i = 1; i <=100 ; i++) {
sum = sum+i;
}
return sum;
}
}
/*
输出:
5050
*/
- 三种实现方式的比较
- Thread类中常见的成员方法
三. 线程同步的方式
- 同步代码块中synchronized后面的锁对象一定要是唯一的(通常是synchronized (MyThread.class))。
- 同步方法就是把synchronized关键字加到方法上。
- 手动上锁–Lock锁
四. 等待唤醒机制
- 等待唤醒机制也叫做生产者和消费者。下面使用吃货(消费者)和厨师(生产者)做个比喻,如下图所示。
- 生产者和消费者常见的方法
- 阻塞队列实现等待唤醒机制,生产者和消费者必须使用同一个阻塞队列。
- 线程的状态
虽然上图中展示了7种状态,但java虚拟机没有定义"运行"转态,所以只有以下6种状态。
五. 线程池
- 线程池原理
- 线程池的代码实现:首先创建线程池;其次提交任务;最后所有的任务全部执行完毕,关闭线程池。
代码实现
public class Test {
public static void main(String[] args) throws IOException {
// 1.创建线程池对象pool1
ExecutorService pool1 = Executors.newCachedThreadPool();
ExecutorService pool2 = Executors.newFixedThreadPool(2); //这个线程池只能存放2个线程
// 2.提交任务
pool1.submit(new MyRunnable());
pool1.submit(new MyRunnable());
pool1.submit(new MyRunnable());
pool1.submit(new MyRunnable());
pool1.submit(new MyRunnable());
pool1.submit(new MyRunnable());
pool2.submit(new MyRunnable());
pool2.submit(new MyRunnable());
pool2.submit(new MyRunnable());
pool2.submit(new MyRunnable());
// 销毁线程池
pool1.shutdown();
pool2.shutdown();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
int sum = 0;
for (int i = 0; i < 100; i++) {
sum = sum+i;
}
System.out.println(Thread.currentThread().getName()+": "+sum);
}
}
输出:
pool-1-thread-1: 4950
pool-1-thread-4: 4950
pool-2-thread-1: 4950
pool-1-thread-2: 4950
pool-2-thread-2: 4950
pool-1-thread-6: 4950
pool-2-thread-1: 4950
pool-2-thread-1: 4950
pool-1-thread-5: 4950
pool-1-thread-3: 4950