每个进程都有独立的代码和数据空间(进程上下文),进程间的切
换会有较大的开销,一个进程包含 1–n 个线程。
同一类线程共享代码和数据空间,每个线程有独立的运行栈和程
序计数器(PC),线程切换开销小。
i. 并行:多个 cpu 实例或者多台机器同时执行一段处理逻辑,是真正
的同时。
ii. 并发:通过 cpu 调度算法,让用户看上去同时执行,实际上从 cpu
操作层面不是真正的同时。并发往往在场景中有公用的资源,那么
针对这个公用的资源往往产生瓶颈,我们会用 TPS 或者 QPS 来反应
这个系统的处理能力。
i. 使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程
对象就处于新建状态。它保持这个状态直到程序 start()这个线程。
i. 当线程对象调用了 start()方法之后,该线程就进入就绪状态,该状态
的线程位于可运行线程池中,变得可运行,等待获取 CPU 的使用权。
就绪状态的线程处于就绪队列中,要等待 JVM 里线程调度器的调度。
i. 如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便
处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状
态、就绪状态和死亡状态。
i. 如果一个线程执行了 sleep(睡眠)、suspend(挂起)等方法,失去
所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间
已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
- 等待阻塞:运行状态中的线程执行 wait()方法,使线程进入到等
待阻塞状态。 - 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别
的线程占用,则 JVM 会把该线程放入锁池中。 - 其他阻塞:通过调用线程的 sleep()或 join()发出了 I/O 请求时,
线程就会进入到阻塞状态。当 sleep()状态超时,join()等待线程
终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
e) 死亡状态:
i. 一个运行状态的线程完成任务或者其他终止条件发生时,该线程就
关为跟你一起学多线程
切换到终止状态。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZmwG8nOz-1658798549782)(C:Users2537376648AppDataRoamingTyporatypora-user-imagesimage-20220416110128231.png)]
线程的实现方式 通过继承 Thread 类package com.dailyblue.java.pack0108;
// 线程的第一种实现方式
// 1⃣️ 继承Thread类
public class DemoA extends Thread {
// 2⃣️ 重新run方法 当run放被执行时 线程进入运行状态
public void run() {
for (int i = 0; i < 100; i++) {
// 获取到当前运行的线程名
String threadName = Thread.currentThread().getName();
System.out.println(threadName + ":" + i);
}
// run方法全部执行完毕后线程进入死亡状态 释放当前线程对象
}
public static void main(String[] args) {
// 当产生一个线程类型(Thread类或者它的子类)的对象时 进入新建状态
DemoA a1 = new DemoA(); // 产生了一个对象
DemoA a2 = new DemoA();
// 当线程对象调用了start方法时 进入就绪状态
// 调用了a1的start方法
a1.start();// 我准备好,只欠东风 只等待CPU执行
a2.start();
}
}
b) 通过实现 Runnable 接口
package com.dailyblue.java.pack0108;
// 线程的第二种实现方式
// 1⃣️ 实现一个Runnable接口
public class DemoB implements Runnable {
// 2⃣️ 重写run方法
public void run() {
for (int i = 1; i <= 20; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
public static void main(String[] args) {
// 这个时候不是新建状态 并不是一个线程对象
DemoB b1 = new DemoB();
DemoB b2 = new DemoB();
// 产生2个线程去包装这两个任务b1,b2
// 新建状态
Thread t1 = new Thread(b1);
Thread t2 = new Thread(b2);
t1.start();
t2.start();
}
}
c) 通过 Callable 和 Future 创建线程
i. 创建 Callable 接口的实现类,并实现 call()方法,该 call()方法将作为
线程执行体,并且有返回值(类似于 run()方法)。
ii. 创建Callable实现类的实例,使用FutureTask类来包装Callable对象,
该 FutureTask 对象封装了该 Callable 对象的 call()方法的返回值。
iii. 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
iv. 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。
package com.dailyblue.java.pack0108;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
// 线程的第三种实现方式
// 1⃣️ 实现一个Callable接口
public class DemoC implements Callable {
// 2⃣️重写call方法 这个方法能够得到返回结果 其他跟run一致
public Object call() throws Exception {
int sum = 0;
for (int i = 1; i <= 20; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
sum += i;
}
return "1-20之和:" + sum;
}
public static void main(String[] args) throws Exception{
DemoC c1 = new DemoC();
DemoC c2 = new DemoC();
FutureTask ft1 = new FutureTask(c1);
FutureTask ft2 = new FutureTask(c2);
Thread t1 = new Thread(ft1);
Thread t2 = new Thread(ft2);
t1.start();
t2.start();
// 获取到返回结果
Object result1 = ft1.get();
Object result2 = ft2.get();
System.out.println(result1);
System.out.println(result2);
}
}
线程的安全和同步
线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。
线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据
package com.dailyblue.java.pack0108;
// 演示了线程安全问题----不安全的情况---如果通过上锁的方式去实现了线程安全
public class DemoE {
public static void main(String[] args) {
Counter counter = new Counter();
for (int i = 0; i < 4; i++) {
DemoE1 e1 = new DemoE1(counter);
Thread t = new Thread(e1);
t.start();
}
// 暂停3s之后执行后续代码
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int result = counter.getCount();
System.out.println(result);
}
}
// 实现了一个线程任务
class DemoE1 implements Runnable {
// 声明了一个counter属性 然后通过构造器给属性赋值
private Counter counter;
public DemoE1(Counter counter){
this.counter = counter;
}
public void run() {
// 循环1000执行addCount() 实现对count的自增
for (int i = 0; i < 1000; i++) {
counter.addCount(); // 自增
}
}
}
// 是一个计数器类
class Counter {
private int count = 0; // 计数器
// 调用一次这个方法
// 需要在某一个线程执行这个方法时 其他线程不能执行 可以通过来实现 synchronized:上锁
public synchronized void addCount() {
count++; // 自增
}
public int getCount() {
return count;
}
}
synchronized的用法
package com.dailyblue.java.pack0108;
// synchronized关键字的使用场景 同步锁
public class DemoF {
private int count = 10;
// 对当前方法上锁的方式 保证线程安全 场景一
public synchronized void methodA() {
System.out.println(Thread.currentThread().getName()+"开始执行递减操作....");
if (count > 0) {
System.out.println(Thread.currentThread().getName() + "执行了相减操作,目前值:" + (count--));
return;
}
System.out.println("count值必须大于等于0!");
}
private Object obj = new Object();
public void methodB() {
System.out.println(Thread.currentThread().getName()+"开始执行递减操作....");
// 对一段代码块进行上锁操作,代码块中需要填写Object类的一个对象 场景二
synchronized (obj){
if (count > 0) {
System.out.println(Thread.currentThread().getName() + "执行了相减操作,目前值:" + (count--));
return;
}
}
System.out.println("count值必须大于等于0!");
}
public static void main(String[] args) {
DemoF f = new DemoF();
for (int i = 0; i < 4; i++) {
DemoF1 f1 = new DemoF1(f);
Thread t1 = new Thread(f1);
t1.start();
}
}
}
class DemoF1 implements Runnable {
private DemoF f;
public DemoF1(DemoF f) {
this.f = f;
}
public void run() {
for (int i = 0; i < 10; i++) {
f.methodA();
}
}
}
ThreadLocal的用法
package com.dailyblue.java.pack0108;
// 一个关键字 ThreadLocal
public class DemoI {
public static void main(String[] args) {
DemoI2 i2 = new DemoI2();
for (int i = 0; i < 4; i++) {
DemoI1 i1 = new DemoI1(i2);
Thread t = new Thread(i1);
t.start();
}
}
}
class DemoI1 implements Runnable {
private DemoI2 i2;
public DemoI1(DemoI2 i2) {
this.i2 = i2;
}
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i2.getCount());
}
}
}
class DemoI2 {
private int count = 0; // 序列号
// 代表这个容器只能存放int类型
private ThreadLocal threadLocal = new ThreadLocal() {
// 重写了一个方法,这个方法是初始化的方法 初始化容器中的值
protected Integer initialValue() {
return 0; // 设置初始值为0
}
};
public int getCount() {
count = threadLocal.get(); // 获取到容器存放的值
count++;
threadLocal.set(count); // 覆盖容器中的值
return count;
}
}
线程的死锁
简单来说,就是两个或两个以上的线程,在执行过程中去争夺一个共享的资源,导致互相等待的现象,如果没有外部干预的情况就会处于一直等待的现象。
产生死锁必须具备以下四个条件:
-
互斥条件:该资源任意一个时刻只由一个线程占用。
-
请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
-
不剥夺条件:线程已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
-
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
如何避免死锁 -
破坏互斥条件
这个条件我们没有办法破坏,因为我们用锁本来就是想让他们互斥的(临界资源需要互斥访问)。
破坏请求与保持条件
一次性申请所有的资源。
破坏不剥夺条件
占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
破坏循环等待条件
靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。
我们对线程 2 的代码修改成下面这样就不会产生死锁了。
package com.dailyblue.java.pack0108; // 死锁的问题 常见面试题 public class DemoG { public static void main(String[] args) { DemoG3 g3 = new DemoG3(); DemoG4 g4 = new DemoG4(); g3.start(); g4.start(); } } class DemoG1 { public synchronized static void a(){ for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+":"+i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } // 调用了另一个上锁方法 DemoG2.b(); } } class DemoG2 { public synchronized static void b(){ for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+":"+i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } // 调用了另一个上锁方法 DemoG1.a(); } } class DemoG3 extends Thread{ @Override public void run() { DemoG1.a(); } } class DemoG4 extends Thread{ @Override public void run() { DemoG2.b(); } }
package com.dailyblue.java.pack0108;
public class DemoH {
public static void main(String[] args) {
Ticket ticket = new Ticket();
for (int i = 0; i < 4; i++) {
DemoH1 h1 = new DemoH1(ticket);
Thread thread = new Thread(h1);
thread.start();
}
}
}
class DemoH1 implements Runnable {
private Ticket ticket;
public DemoH1(Ticket ticket) {
this.ticket = ticket;
}
public void run() {
for (int i = 0; i < 30; i++) {
ticket.purchase(); // 购票
}
}
}
class Ticket {
private int ticketAllNum = 100; // 总票数
private int ticketIndex = 1; // 票号
public void purchase() {
synchronized (new Object()){
if (ticketAllNum > 0) {
System.out.println(Thread.currentThread().getName() + "购买了" + ticketIndex + "号");
ticketIndex++;
ticketAllNum--;
return;
}
System.out.println("尊敬的," + Thread.currentThread().getName() + ",卖完了!");
}
}
}
生产者和消费者
public class Test {
public static void main(String[] args) {
商店 sd = new 商店();
生产者 scz = new 生产者(sd);
消费者 xfz = new 消费者(sd);
scz.start();
xfz.start();
}
}
// 描述了一个产品
public class 产品 {
private Integer id;
public 产品(Integer id) {
this.id = id;
}
@Override
public String toString() {
return "产品{" +
"id=" + id +
'}';
}
}
// 负责从生产者哪里进货,负责将产品卖给消费者
public class 商店 {
// 描述了库存,最多存放10个产品
private 产品[] 库存 = new 产品[10];
private int index = 0;
private int 实际库存量 = 0;
// 这个会被生产者调用 ,将产品传递过来
public synchronized void 进货(产品 cp) {
if (实际库存量 == 10) {
// 库存满了,不能存放了
System.out.println("库存满了,请不要在进货了!");
try {
wait(); // 让当前线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 将产品放入到库存中
库存[index] = cp;
index++;
实际库存量++;
notify(); // 生产者发货了 商店就有产品了 唤醒消费者的线程
}
public synchronized 产品 卖出(){
if(实际库存量==0){
System.out.println("商店当前缺货,请稍等!");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 获取到库存中的最后一个产品
产品 cp = 库存[index-1];
// 删除这个产品
index--;
实际库存量--;
notify(); // 当消费者购买产品后 库存减少了 就可以继续进货了 唤醒生产者的线程
return cp;
}
}
// 负责消费产品
public class 消费者 extends Thread{
private 商店 sd;
public 消费者(商店 sd) {
this.sd = sd;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
产品 cp = sd.卖出();
System.out.println("购买了"+cp);
try {
Thread.sleep(1000); // 每隔3s消费一个产品
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 负责生产产品
public class 生产者 extends Thread{
private 商店 sd;
public 生产者(商店 sd) {
this.sd = sd;
}
@Override
public void run() {
// 负责生产产品
for (int i = 0; i < 20; i++) {
产品 cp = new 产品(i+1); // 生产产品
sd.进货(cp);
System.out.println("生产了产品:" + cp);
try {
Thread.sleep(3000); // 每隔1s生产一个产品
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
线程的四个方法
sleep方法
// sleep方法,让当前线程睡眠一会,进入到阻塞状态,它不会释放锁资源,仍然持有,其他线程仍然无法访问
public class DemoA {
public static void main(String[] args) {
DemoA3 a3 = new DemoA3();
DemoA2 a2 = new DemoA2(a3);
DemoA1 a1 = new DemoA1(a3);
Thread t1 = new Thread(a1);
Thread t2 = new Thread(a2);
// 启动2个线程 只是让这两个线程进入就绪状态
t1.start();
t2.start();
}
}
class DemoA1 implements Runnable{
private DemoA3 a3;
public DemoA1(DemoA3 a3) {
this.a3 = a3;
}
public void run() {
a3.a();
}
}
class DemoA2 implements Runnable{
private DemoA3 a3;
public DemoA2(DemoA3 a3) {
this.a3 = a3;
}
public void run() {
a3.a();
}
}
class DemoA3{
public synchronized void a(){
System.out.println("我是a方法,现在被"+Thread.currentThread().getName()+"这个线程在执行");
try {
Thread.sleep(10000);//让线程阻塞10s
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我的阻塞原因解除,继续被"+Thread.currentThread().getName()+"这个线程执行");
}
}
wait方法
// wait方法 让当前线程等待,也会进入阻塞状态,它是Object类的一个方法,它会释放锁资源,其他线程可以访问这个资源
// wait方法只能在同步块中使用,也就是说只能在有锁资源中使用
public class DemoB {
public static void main(String[] args) {
DemoB3 b3 = new DemoB3();
DemoB1 b1 = new DemoB1(b3);
DemoB2 b2 = new DemoB2(b3);
Thread t1 = new Thread(b1);
Thread t2 = new Thread(b2);
t1.start();
t2.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
DemoB4 b4 = new DemoB4(b3);
Thread t4 = new Thread(b4);
t4.start();
}
}
class DemoB1 implements Runnable{
private DemoB3 a3;
public DemoB1(DemoB3 a3) {
this.a3 = a3;
}
public void run() {
a3.b();
}
}
class DemoB2 implements Runnable{
private DemoB3 a3;
public DemoB2(DemoB3 a3) {
this.a3 = a3;
}
public void run() {
a3.b();
}
}
class DemoB4 implements Runnable{
private DemoB3 a3;
public DemoB4(DemoB3 a3) {
this.a3 = a3;
}
public void run() {
a3.c(); // 唤醒
}
}
class DemoB3{
public synchronized void a(){
System.out.println("我是a方法,现在被"+Thread.currentThread().getName()+"这个线程在执行");
try {
wait(10000);// 让当前线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我的阻塞原因解除,继续被"+Thread.currentThread().getName()+"这个线程执行");
}
public synchronized void b(){
System.out.println("我是a方法,现在被"+Thread.currentThread().getName()+"这个线程在执行");
try {
wait();// 让当前线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我的阻塞原因解除,继续被"+Thread.currentThread().getName()+"这个线程执行");
}
public synchronized void c(){
notifyAll(); // 唤醒其他等待的线程
}
}
yield方法
public class DemoC {
public static void main(String[] args) {
DemoC3 c3 = new DemoC3();
DemoC1 c1 = new DemoC1(c3);
DemoC2 c2 = new DemoC2(c3);
Thread t1 = new Thread(c1);
Thread t2 = new Thread(c2);
t1.setPriority(4);
t2.setPriority(4);
t1.start();
t2.start();
}
}
class DemoC1 implements Runnable{
private DemoC3 c3;
public DemoC1(DemoC3 c3) {
this.c3 = c3;
}
public void run() {
c3.b();
}
}
class DemoC2 implements Runnable{
private DemoC3 c3;
public DemoC2(DemoC3 c3) {
this.c3 = c3;
}
public void run() {
c3.b();
}
}
class DemoC3{
public void a(){
System.out.println("我是a方法,现在被"+Thread.currentThread().getName()+"这个线程在执行");
Thread.yield();//只是让当前线程暂停,让别的线程先执行
System.out.println("我的阻塞原因解除,继续被"+Thread.currentThread().getName()+"这个线程执行");
}
public synchronized void b(){
System.out.println("我是b方法,现在被"+Thread.currentThread().getName()+"这个线程在执行");
Thread.yield();//只是让当前线程暂停,让别的线程先执行
System.out.println("我的阻塞原因解除,继续被"+Thread.currentThread().getName()+"这个线程执行");
}
}
join方法
public class DemoD {
private static final Object obj = new Object();
private static int count = 0; // 计数器
public static void main(String[] args) throws Exception{
DemoD1 d1 = new DemoD1();
DemoD1 d2 = new DemoD1();
Thread t1 = new Thread(d1);
Thread t2 = new Thread(d2);
t1.start();
t2.start();
t1.join(); // 等待调用join方法的线程结束后,程序才能继续执行
t2.join();
// 这段代码必须等待t1和t2线程结束后才能执行
System.out.println("最后的结果是:"+count);
}
static class DemoD1 implements Runnable{
@Override
public void run() {
synchronized (obj){
System.out.println(Thread.currentThread().getName() + "准备执行");
for (int i = 0; i < 10; i++) {
count++;
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "结束执行");
}
}
}
}
四个方法的区别
sleep方法,让当前线程睡眠一会,进入到阻塞状态,它不会释放锁资源,仍然持有,其他线程仍然无法访问
// wait方法 让当前线程等待,也会进入阻塞状态,它是Object类的一个方法,它会释放锁资源,其他线程可以访问这个资源
// wait方法只能在同步块中使用,也就是说只能在有锁资源中使用
yield方法,暂停当前正在执行的线程,不会释放锁资源,不会进入阻塞状态,暂停结束后继续运行 ,应该在锁资源访问
但是,实际上无法保证yield达到让步的目的,因为让步的线程还有可能被线程调度再次选中
join方法 等待调用join方法的线程结束后,程序才能继续运行
守护线程不管程序中守护线程是否执行完毕,只要所有的非守护线程执行完毕,程序结束。
// 守护(后台)线程
public class DemoE {
public static void main(String[] args) {
// 只要e1线程执行完毕了,就让当前程序结束
DemoE1 e1 = new DemoE1();
DemoE2 e2 = new DemoE2();
// 只需要设置e2线程为守护线程 当程序中的非守护线程结束后 程序结束
e2.setDaemon(true);
e1.start();
e2.start();
}
}
class DemoE1 extends Thread {
@Override
public void run() {
for (int i = 1; i <= 20; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class DemoE2 extends Thread{
@Override
public void run() {
int i = 1;
while(true){
System.out.println(Thread.currentThread().getName() + ":" + i);
i++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
线程池
概念:如果并发的线程数量很多,并且每个线程都是执行一个时间很短
的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频
繁创建线程和销毁线程需要时间。那么有没有一种办法使得线程可以复
用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务?
在 Java 中可以通过线程池来达到这样的效果。
i. newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过
处理需要,可灵活回收空闲线程,若无可回收,则新建线程。这种
类型的线程池特点是
-
工作线程的创建数量几乎没有限制(其实也有限制的,数目为
Interger.MAX_VALUE),这样可灵活的往线程池中添加线程。 -
如果长时间没有往线程池中提交任务,即如果工作线程空闲了指
定的时间(默认为 1 分钟),则该工作线程将自动终止。终止后,
如果你又提交了新的任务,则线程池重新创建一个工作线程。 -
在使用 CachedThreadPool 时,一定要注意控制任务的数量,否
则,由于大量线程同时运行,很有会造成系统瘫痪。
ExecutorService cached = Executors.newCachedThreadPool();
Cached.execte(Runnable 的实现对象);
线程池为无限大,当执行第二个任务时第一个任务已经完成,会复
用执行第一个任务的线程,而不用每次新建线程。
ii. newFixedThreadPool:创建一个定长线程池,可控制线程最大并发数,
每当提交一个任务就创建一个工作线程,如果工作线程数量达到线
程 池 初 始 的 最 大 数 , 则 将 提 交 的 任 务 存 入 到 池 队 列 中 。
FixedThreadPool 是一个典型且优秀的线程池,它具有线程池提高程
序效率和节省创建线程时所耗的开销的优点。但是,在线程池空闲
时,即线程池中没有可运行任务时,它不会释放工作线程,还会占
用一定的系统资源。
ExecutorService fixed= Executors.newFixedThreadPool(5);
fixed.execte(Runnable 的实现对象);
线程池大小为 5,这个参数指定了可以运行的线程的最大数目,超过
这个数目的线程加进去以后,不会运行。其次,加入线程池的线程
属于托管状态,线程的运行不受加入顺序的影响。
关为跟你一起学多线程
iii. newSingleThreadExecutor:创建一个单线程化的 Executor,即只创建
唯一的工作者线程来执行任务,它只会用唯一的工作线程来执行任
务,保证所有任务按照指定顺序(FIFO, LIFO,优先级)执行。如果这个
线程异常结束,会有另一个取代它,保证顺序执行。单工作线程最
大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不
会有多个线程是活动的。
ExecutorService sin= Executors.newSingleThreadExecutor();
sin.execte(Runnable 的实现对象);
不管放入多少个 Runnable 实现对象,执行的都是一个线程
iv. newScheduleThreadPool:创建一个定长的线程池,而且支持定时的
以及周期性的任务执行,支持定时及周期性任务执行。(一般被定时
器替代,不在重复说明了)
ExecutorService 提供了管理 Eecutor 生命周期的方法,ExecutorService 的
生命周期包括了:运行、关闭和终止三种状态。
ExecutorService 在初始化创建时处于运行状态。
shutdown 方法等待提交的任务执行完成并不再接受新任务,在完成全部
提交的任务后关闭
shutdownNow方法将强制终止所有运行中的任务并不再允许提交新任务
答:只有调用了 start()方法,才会表现出多线程的特性,不同线程的 run()
方法里面的代码交替执行。如果只是调用 run()方法,那么代码还是同步
执行的,必须等待一个线程的 run()方法里面的代码全部执行完毕之后,
另外一个线程才可以执行其 run()方法里面的代码。
b) Runnable 接口和 Callable 接口的区别?
答:Runnable 接口中的 run()方法的返回值是 void,它做的事情只是纯粹
地去执行 run()方法中的代码而已;Callable 接口中的 call()方法是有返回
值的,是一个泛型,和 Future、FutureTask 配合可以用来获取异步执行的
结果。
这其实是很有用的一个特性,因为多线程相比单线程更难、更复杂的一
个重要原因就是因为多线程充满着未知性,某条线程是否执行了?某条
线程执行了多久?某条线程执行的时候我们期望的数据是否已经赋值完
毕?无法得知,我们能做的只是等待这条多线程的任务执行完毕而已。
而 Callable+Future/FutureTask 却可以获取多线程运行的结果,可以在等
待时间太长没获取到需要的数据的情况下取消该线程的任务,真的是非
常有用。
答:sleep 方法和 wait 方法都可以用来放弃 CPU 一定的时间,不同点在
于如果线程持有某个对象的监视器(监视对象同步),sleep 方法不会放
弃这个对象的监视器,wait 方法会放弃这个对象的监视器,并且 wait()
只能在同步中使用
关为跟你一起学多线程
答:有的时候,在我们处理一些大量的短小任务时,如果不使用线程池,
则每次 new Thread 新建对象性能差;线程缺乏统一管理,可能无限制新
建线程,相互之间竞争,及可能占用过多系统资源导致死机;缺乏更多
功能,如定时执行、定期执行、线程中断。
那么当我们使用线程池时,重用存在的线程,减少对象创建、消亡的开
销,性能佳;可有效控制最大并发线程数,提高系统资源的使用率,同
时避免过多资源竞争,避免堵塞;提供定时执行、定期执行、单线程、
并发数控制等功能。
答:shutDown()
当线程池调用该方法时,线程池的状态则立刻变成 SHUTDOWN 状态。此
时 , 则 不 能 再 往 线 程 池 中 添 加 任 何 任 务 , 否 则 将 会 抛 出
RejectedExecutionException 异常。但是,此时线程池不会立刻退出,直
到添加到线程池中的任务都已经处理完成,才会退出。
shutdownNow()
根据 JDK 文档描述,大致意思是:执行该方法,线程池的状态立刻变成
STOP 状态,并试图停止所有正在执行的线程,不再处理还在池队列中等
待的任务,当然,它会返回那些未执行的任务。它试图终止线程的方法
是通过调用 Thread.interrupt()方法来实现的,但是大家知道,这种方法的
作用有限,如果线程中没有 sleep 、wait、Condition、定时锁等应用,
interrupt()方法是无法中断当前的线程的。所以,ShutdownNow()并不代
表线程池就一定立即就能退出,它可能必须要等待所
package com.dailyblue.java.pack0109;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// 线程池的第一种实现方式:单线程池
// 我这个池子里有且仅有一个线程,不管多少任务 都是这个线程负责处理
public class DemoF {
public static void main(String[] args) {
// 产生一个单线程的线程池
ExecutorService service = Executors.newSingleThreadExecutor();
// 模拟了100个任务
for (int i = 0; i < 100; i++) {
DemoF1 f1 = new DemoF1();
service.execute(f1); // 加入线程池
}
}
}
class DemoF1 implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行了任务!");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package com.dailyblue.java.pack0109;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// 线程池的第2种实现方式:定长线程池 最重要的
public class DemoG {
public static void main(String[] args) {
// 产生一个定长线程池,线程个数是固定的 有4个线程
ExecutorService service = Executors.newFixedThreadPool(4);
for (int i = 0; i < 100; i++) {
DemoG1 g1 = new DemoG1();
service.execute(g1);
}
}
}
class DemoG1 implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行了任务!");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package com.dailyblue.java.pack0109;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// 线程池的第3种实现方式:可缓存线程池
public class DemoH {
public static void main(String[] args) {
// 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程和创建新线程,若无可回收,则新建线程。
ExecutorService service = Executors.newCachedThreadPool();
for (int i = 0; i < 100; i++) {
DemoH1 g1 = new DemoH1();
service.execute(g1);
try {
Thread.sleep(3500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class DemoH1 implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行了任务!");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package com.dailyblue.java.pack0109;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
// 线程池的第4种实现方式:定时线程池
public class DemoI {
public static void main(String[] args) {
// 产生一个定时器的线程池
ScheduledExecutorService service = Executors.newScheduledThreadPool(4);
DemoI1 di = new DemoI1();
// 延迟5s后开始执行任务di
// service.schedule(di,5, TimeUnit.SECONDS);
// 延迟5s后开始执行任务di 并且每隔1s后在执行一次
service.scheduleAtFixedRate(di,5,1,TimeUnit.SECONDS);
}
}
// 被执行的任务
class DemoI1 implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行了任务!");
}
}
package com.dailyblue.java.pack0109;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
// shutdown和shutdownNow的区别
public class DemoJ {
public static void main(String[] args) {
a();
}
public static void a(){
ExecutorService service = Executors.newFixedThreadPool(2);
for (int i = 0; i < 20; i++) {
DemoJ1 j1 = new DemoJ1();
service.execute(j1);
}
service.shutdown(); // 关闭线程池
try {
boolean b = service.awaitTermination(10, TimeUnit.SECONDS); // 判断当前线程池状态的
System.out.println(b);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("执行了shutdown()");
}
public static void b(){
ExecutorService service = Executors.newFixedThreadPool(2);
for (int i = 0; i < 20; i++) {
DemoJ1 j1 = new DemoJ1();
service.execute(j1);
}
service.shutdownNow(); // 关闭线程池
try {
boolean b = service.awaitTermination(10, TimeUnit.SECONDS); // 判断当前线程池状态的
System.out.println(b);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("执行了shutdownNow()");
}
}
class DemoJ1 implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行了任务!");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package com.dailyblue.java.pack0109;
// start和run
public class DemoK {
public static void main(String[] args) {
DemoK1 k1 = new DemoK1();
DemoK1 k2 = new DemoK1();
// 方法调用 没有多线程的特点
k1.run();
k2.run();
}
}
class DemoK1 extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package com.dailyblue.java.pack0109;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
// 定时器 TimeTask定时任务 另外一个定时器的实现方式
public class DemoL {
public static void main(String[] args) {
// 产生了一个定时任务对象
DemoL1 l1 = new DemoL1();
// 负责执行定时任务的对象
Timer timer = new Timer();
// timer.schedule(l1,5000); // 5s之后执行l1任务
// timer.schedule(l1,5000,1000);// 5s之后执行l1任务 每隔1s执行一次
// 在指定时间到了后,执行任务
Date date = new Date(122,0,9,16,38,0);
timer.schedule(l1,date);
}
}
class DemoL1 extends TimerTask {
@Override
public void run() {
// 定时任务
System.out.println(Thread.currentThread().getName() + "执行了这个方法!");
}
}
package com.dailyblue.java.pack0109;
import java.util.concurrent.CountDownLatch;
public class DemoM {
public static void main(String[] args) {
CountDownLatch cdl = new CountDownLatch(4);
for (int i = 0; i < 4; i++) {
DemoM1 m1 = new DemoM1(cdl);
Thread t1 = new Thread(m1);
t1.start();
}
System.out.println("问候的这一段内容必须等待所有4个线程结束后执行");
try {
cdl.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(" ");
}
}
class DemoM1 implements Runnable{
private CountDownLatch cdl;
public DemoM1(CountDownLatch cdl) {
this.cdl = cdl;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在运行");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"结束运行");
// 标注线程结束了
cdl.countDown(); // 标记线程结束
}
}
System.out.println(Thread.currentThread().getName() + "执行了这个方法!"); }
}
```java
package com.dailyblue.java.pack0109;
import java.util.concurrent.CountDownLatch;
public class DemoM {
public static void main(String[] args) {
CountDownLatch cdl = new CountDownLatch(4);
for (int i = 0; i < 4; i++) {
DemoM1 m1 = new DemoM1(cdl);
Thread t1 = new Thread(m1);
t1.start();
}
System.out.println("问候的这一段内容必须等待所有4个线程结束后执行");
try {
cdl.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(" ");
}
}
class DemoM1 implements Runnable{
private CountDownLatch cdl;
public DemoM1(CountDownLatch cdl) {
this.cdl = cdl;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在运行");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"结束运行");
// 标注线程结束了
cdl.countDown(); // 标记线程结束
}
}



