栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

java多线程

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

java多线程

Java多线程 线程的基本概念 进程:

每个进程都有独立的代码和数据空间(进程上下文),进程间的切
换会有较大的开销,一个进程包含 1–n 个线程。

b) 线程:

同一类线程共享代码和数据空间,每个线程有独立的运行栈和程
序计数器(PC),线程切换开销小。

) 并行与并发

i. 并行:多个 cpu 实例或者多台机器同时执行一段处理逻辑,是真正
的同时。
ii. 并发:通过 cpu 调度算法,让用户看上去同时执行,实际上从 cpu
操作层面不是真正的同时。并发往往在场景中有公用的资源,那么
针对这个公用的资源往往产生瓶颈,我们会用 TPS 或者 QPS 来反应
这个系统的处理能力。

线程的生命周期 新建状态:

i. 使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程
对象就处于新建状态。它保持这个状态直到程序 start()这个线程。

b) 就绪状态:

i. 当线程对象调用了 start()方法之后,该线程就进入就绪状态,该状态
的线程位于可运行线程池中,变得可运行,等待获取 CPU 的使用权。
就绪状态的线程处于就绪队列中,要等待 JVM 里线程调度器的调度。

c) 运行状态:

i. 如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便
处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状
态、就绪状态和死亡状态。

d) 阻塞状态:

i. 如果一个线程执行了 sleep(睡眠)、suspend(挂起)等方法,失去
所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间
已到或获得设备资源后可以重新进入就绪状态。可以分为三种:

  1. 等待阻塞:运行状态中的线程执行 wait()方法,使线程进入到等
    待阻塞状态。
  2. 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别
    的线程占用,则 JVM 会把该线程放入锁池中。
  3. 其他阻塞:通过调用线程的 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;
    }
}
线程的死锁

简单来说,就是两个或两个以上的线程,在执行过程中去争夺一个共享的资源,导致互相等待的现象,如果没有外部干预的情况就会处于一直等待的现象。

产生死锁必须具备以下四个条件:

  1. 互斥条件:该资源任意一个时刻只由一个线程占用。

  2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。

  3. 不剥夺条件:线程已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。

  4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

    如何避免死锁
  5. 破坏互斥条件

    这个条件我们没有办法破坏,因为我们用锁本来就是想让他们互斥的(临界资源需要互斥访问)。

    破坏请求与保持条件

    一次性申请所有的资源。

    破坏不剥夺条件

    占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。

    破坏循环等待条件

    靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。

    我们对线程 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:创建一个可缓存线程池,如果线程池长度超过
处理需要,可灵活回收空闲线程,若无可回收,则新建线程。这种
类型的线程池特点是

  1. 工作线程的创建数量几乎没有限制(其实也有限制的,数目为
    Interger.MAX_VALUE),这样可灵活的往线程池中添加线程。

  2. 如果长时间没有往线程池中提交任务,即如果工作线程空闲了指
    定的时间(默认为 1 分钟),则该工作线程将自动终止。终止后,
    如果你又提交了新的任务,则线程池重新创建一个工作线程。

  3. 在使用 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:创建一个定长的线程池,而且支持定时的
    以及周期性的任务执行,支持定时及周期性任务执行。(一般被定时
    器替代,不在重复说明了)

d) 线程池的生命周期

ExecutorService 提供了管理 Eecutor 生命周期的方法,ExecutorService 的
生命周期包括了:运行、关闭和终止三种状态。
ExecutorService 在初始化创建时处于运行状态。
shutdown 方法等待提交的任务执行完成并不再接受新任务,在完成全部
提交的任务后关闭
shutdownNow方法将强制终止所有运行中的任务并不再允许提交新任务

十一. 多线程中的常见问题 a) Run()和 start()之间的区别?

答:只有调用了 start()方法,才会表现出多线程的特性,不同线程的 run()
方法里面的代码交替执行。如果只是调用 run()方法,那么代码还是同步
执行的,必须等待一个线程的 run()方法里面的代码全部执行完毕之后,
另外一个线程才可以执行其 run()方法里面的代码。
b) Runnable 接口和 Callable 接口的区别?
答:Runnable 接口中的 run()方法的返回值是 void,它做的事情只是纯粹
地去执行 run()方法中的代码而已;Callable 接口中的 call()方法是有返回
值的,是一个泛型,和 Future、FutureTask 配合可以用来获取异步执行的
结果。
这其实是很有用的一个特性,因为多线程相比单线程更难、更复杂的一
个重要原因就是因为多线程充满着未知性,某条线程是否执行了?某条
线程执行了多久?某条线程执行的时候我们期望的数据是否已经赋值完
毕?无法得知,我们能做的只是等待这条多线程的任务执行完毕而已。
而 Callable+Future/FutureTask 却可以获取多线程运行的结果,可以在等
待时间太长没获取到需要的数据的情况下取消该线程的任务,真的是非
常有用。

c) Sleep()方法和 wait()方法的区别?

答:sleep 方法和 wait 方法都可以用来放弃 CPU 一定的时间,不同点在
于如果线程持有某个对象的监视器(监视对象同步),sleep 方法不会放
弃这个对象的监视器,wait 方法会放弃这个对象的监视器,并且 wait()
只能在同步中使用

d) 为什么使用线程池?

关为跟你一起学多线程
答:有的时候,在我们处理一些大量的短小任务时,如果不使用线程池,
则每次 new Thread 新建对象性能差;线程缺乏统一管理,可能无限制新
建线程,相互之间竞争,及可能占用过多系统资源导致死机;缺乏更多
功能,如定时执行、定期执行、线程中断。
那么当我们使用线程池时,重用存在的线程,减少对象创建、消亡的开
销,性能佳;可有效控制最大并发线程数,提高系统资源的使用率,同
时避免过多资源竞争,避免堵塞;提供定时执行、定期执行、单线程、
并发数控制等功能。

e) shutdown()和 shutdownNow()的区别?

答: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(); // 标记线程结束
    }
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1027860.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号