一、并行、并发和串行
并行:两个或者多个事件在同一时刻发生;(不同实体)
并发:两个或多个事件在同一事件间隔发生;(同一个实体)
串行:同一时刻一个CPU只能处理一件事;
二、进程与线程
进程:程序运行和资源分配的基本单位
线程:进程的一个实体,CPU调度和分派的基本单位,同一进程中的多个线程可以并发执行。
三、线程的状态
线程生命周期,五种状态:
1、新建状态(new):当线程对象创建后就进入了新建状态,如:Thread t = new Thread();
2、就绪状态(Runnable):当调用线程对象的start()方法,线程即为进入就绪状态。
处于就绪(可运行)状态的线程,只是说明线程已经做好准备,随时等待CPU调度状态,并不是执行了t.start()此线程立即就会执行。
3、运行状态(Running):当CPU调度了处于就绪状态的线程时,此线程才是真正的执行,即进入到运行状态。
就绪状态是进入运行状态的唯一入口,也就是线程想要进入运行状态,状态执行,先得处于就绪状态。
4、阻塞状态(Blocked):处于状态中的线程由于某种原因,暂时放弃对CPU的使用权。停止执行,此时进入阻塞状态,直到其进入就绪状态才有机会被CPU选中再次执行。
三种阻塞状态:
1)等待阻塞:运行状态中的线程执行wait()方法,本线程进入到等待阻塞状态。
2)同步阻塞:线程在获取synchronized同步锁失败(因为锁被其他线程占用),会进入同步阻塞。
3)其他阻塞:调用线程的sleep()或者join()或发出了I/O请求时,线程会进入到阻塞状态。 当sleep()状态超时,join()等待线程终止或者超时或者I/O处理完毕时线程重新转让就绪状态。
5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
四、多线程创建方式
1、继承Thread类
1)自定义自己的多线程类
2)重写run(),里面是我们自己的业务
3)创建自定义线程类对象
4)通过线程对象 .start(),将线程加入就绪列
5)查看多线程编程的抢占效果
public class TestThread {
public static void main(String[] args) {
//4.创建线程对象进行测试
/*4.new对应的是线程的新建状态*/
/*5.要想模拟多线程,至少得启动2个线程,如果只启动1个,是单线程程序*/
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
MyThread t4 = new MyThread();
/*6.这个tun()如果直接这样调用,是没有多线程抢占执行的效果的
* 只是把业务方法看作是普通方法来调用,谁先写,就先执行谁*/
// t1.run();
// t2.run();
// t3.run();
// t4.run();
/*7.start()对应的状态才是线程的就绪状态,它会把刚刚新建好的线程加入到就绪
* 队列中,至于什么时候执行,就是多线程抢占的效果,需要OS选中分配时间片才会执行
* 8.执行start()时,底层会自动调用我们重写的业务方法run()
* 9.线程的执行具有随机性,也就是说t1-t4具体怎么执行
* 取决于CPU调度时间片的分配规则,我们是决定不了的*/
t1.start();
t2.start();
t3.start();
t4.start();
}
}
//1.自定义一个多线程类
class MyThread extends Thread{
/*1.多线程编程实现的方案1:通过继承Thread类,并重写run()来完成的*/
//2.重写run(),run()里是我们自己的业务
@Override
public void run() {
/*2.super.run()表示这里调用的是父类的业务,但现在我们想用自己的业务,所以注释掉*/
//super.run();
//3.完成业务,打印10次当前正在执行的线程名称
for (int i = 0; i < 10; i++) {
/*3.getName()表示可以获取当前正在执行的线程名称
* 由于本类继承了Thread类,所以可以直接使用Thread类提供的功能*/
System.out.println(i+"="+getName());
}
}
}
2、实现Runnable接口
1)自定义自己的多线程类
2)实现run(),里面是我们自己的业务
3)创建自定义类的对象,作为唯一的业务对象
4)创建多个Thread类对象,作为多线程对象,并将业务对象target传入
5)使用多个线程对象调用start()对象
public class TestThread2 {
public static void main(String[] args) {
//4.创建自定义类的对象--目标业务类对象
MyRunnable target = new MyRunnable();
//5.启动线程,需要与Thread类建立关系
Thread t1 = new Thread(target);//利用的是Thread的构造方法(Runnable target)
Thread t2 = new Thread(target);
Thread t3 = new Thread(target);
Thread t4 = new Thread(target);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
//1.自定义多线程类
class MyRunnable implements Runnable{
//2.添加父接口中未实现的抽象方法run(),里面是自己的业务
@Override
public void run() {
//3.打印10次当前正在执行的线程名称
for (int i = 0; i < 10; i++) {
/*问题:自定义类与父接口Runnable中都没有获取线程名字的方法
* currentThread():先通过这个静态方法,获取当前正在执行的线程对象
* getName():然后获取这个线程对象的名字*/
System.out.println(i+"="+Thread.currentThread().getName());
}
}
}
五、创建线程池的四种方式
1、newFixedThreadPool:创建一个固定长度线程池,可控制线程最大并发数,超出的线程会在队 列中等待。
2、newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
3、newScheduledThreadPool:创建一个固定长度线程池,支持定时及周期性任务执行。
4、newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行。