一,多线程的概念
进程:正在运行的程序
线程:是进程中的一个执行单元(一条执行路径),一个进程中至少包含一个线程。如果一个进程中有多个线程,这样的程序就称为多线程程序。
线程的调度:
注:
二,创建线程
方式一:继承 Thread 类
public class Test1 {
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
MyThread mt2 = new MyThread();
mt2.start();
for(int i = 1;i <= 100;i++){
System.out.println("main方法:"+i);
}
}
}
class MyThread extends Thread{
// 重写run()方法是为了明确线程中所执行的任务
public void run() {
for(int i = 1;i <= 100;i++){
System.out.println(Thread.currentThread().getName()+"---"+i);
}
}
}
方式二:实现 Runnable 接口
public class Test2 {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
Thread t = new Thread(mr);
t.start();
Thread t2 = new Thread(mr);
t2.start();
for(int i = 1;i <= 300;i++){
System.out.println("main方法:"+i);
}
}
}
class MyRunnable implements Runnable{
public void run() {
for(int i = 1;i<=300;i++){
System.out.println(Thread.currentThread()+"--"+i);
}
}
}
继承 Thread 类 和 实现 Runnable 接口的选用:
使用实现的方式,因为避免了单继承的局限性
注:同一个线程对象不能重复开启,重复开启会发生 IllegalThreadStateException
使用匿名内部类的方式创建线程并开启:
new Thread(){
public void run(){
// 线程执行的任务
}
}.start();new Thread(new Runnable(){
public void run(){
// 线程执行的任务
}
}).start();
三,线程中的方法
Thread [ Thread - 0 , 5 , main]
四,线程安全问题
什么是线程安全:如果有多个线程同时执行,这些线程同时操作同一个内容,程序运行后的结果和单线程运行的结果是一样的时候,就称为线程安全。
完成 3 个窗口同时卖票案例时,发现了重复票和负数票的情况,原因是多个线程在操作同一个 ticket 变量,
在某个线程通过了 if 判断后,被其他线程抢夺了 CPU 的执行权,所以 if 后的 ticket-- 操作,在一次判断后重复执行了多次。
解决方式:
格式:
synchronized(锁对象){
// 可能发生线程安全问题的代码
}
注:
步骤:
格式:
public synchronized 返回类型 方法名(参数){
// 可能发生线程安全问题的代码
}
注:
解决办法:使用 static 修饰同步函数,此时同步函数中的锁还是 this 吗?
不是,因为 static 不能访问 this,锁是 类名.class
同步技术的原理:
同步技术中使用到了锁对象,这个锁对象也称为同步锁
多个线程一起抢夺 CPU 的执行权,谁抢到了,谁就能进入同步代码块,
当一个线程抢夺到了 CPU 的执行权时,会判断是否有锁对象,
如果有锁对象,就会获取到这个锁,进入同步代码块,当这个线程执行完同步代码块,就会释放锁
如果没有锁对象,会处于阻塞状态,等待其他线程执行完同步代码块,等待其他线程释放锁,才能再进行 CPU 执行权的抢夺
使用同步锁会影响程序的执行效率:
因为获取 CPU 执行权的线程,在进入同步代码块之前,需要判断是否有锁,
如果有锁还需要进行获取的动作,
在执行完同步代码块后,需要释放锁。
(判断锁,获取锁,释放锁)
五,线程池
5.1 问题
在每次使用线程时,都需要创建线程对象,操作方式非常简便,但是存在一个问题:
如果并发的线程数量很多,每个线程执行完一个时间很短的任务就结束了,这样频繁创建线程会极大的降低程序的性能。
所以我们需要想一个办法能重复利用已创建的线程,来避免线程的过多创建。
5.2 概念
线程池本质上就是一个可以存放多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作。
5.3 好处
5.4 实现步骤