java多线程

一. 多线程概述

1.1 什么是线程

进程概念:进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位。
线程概念:线程是进程的一个执行路径,一个进程中至少有一个线程,进程中的多个线程共享进程的资源。
多线程概念:指从软硬件上实现的多条执行流程的技术。

1.2 多线程用在那里,有什么好处

例如:多个用户想同时实现同一个功能, 或者在发送消息的同时也要接受消息, 等等

二. 多线程的创建方法, 各有什么优势

2.1 继承Thread类

java是通过Thread类来表示线程的,在java.lang包下。
通过继承Thread类,并重载run方法,当使用start方法时(通知cpu,我是单独的一个线程),就是开启了一条线程了。而当调用的是run方法时,只会被当成是普通方法,还是单线程。

注意:主线程在start方法之前的任务并不会并发执行,只有布置在start方法之后的任务才将并发执行。

优点:编码简单
缺点:线程类已经继承了Thread类,无法继承其他的类,不利于扩展。

2.2 实现Runnable接口

该线程类需要先实现Runnable接口,并实现他的run方法;
使用时要先创建该线程类的实例,然后将该实例作为参数new Thread§.start;

补充:Thread的三种构造器

public Thread ( String name ); 可以为当前线程指定名称
public Thread (Runnable p); 封装Runnable对象成为线程对象。
public Thread (Runnable p,String name); 封装Runnable对象成为线程对象,并指定线程名称。

优点:线程类只是实现了Runnable接口,依然可以继续去继承或者实现其他的接口,扩展性强。
缺点:多了一层对象包装,如果线程有执行结果是不可以直接返回的。

2.3 jdk5.0之后新增:实现Callable接口

前两种和创建方式都存在一个问题,就是重载的run方法都不能直接返回结果,就不适合在需要返回线程执行结果的业务场景。

使用方法:
任务对象实现Callable接口(需要制定返回值的范型),重写call方法,封装任务。
使用FutureTask把Callable对象封装成线程任务对象。(FutureTask其实也是Runnable对象,也要指定范型)
再将FutureTask的对象给Thread对象。
Thread对象使用start方法开启线程任务。
FutureTask对象的get方法可以得到call方法的结果。(可以在此方法try/catch,还能得到异常的结果)

优点: 线程任务类只是实现接口, 扩展性强
可以在线程执行完成之后去获取线程执行的结果
缺点: 编码复杂.

三. 线程的常用方法

Thread常用的API有:

  • getName(); 获取线程的名称
  • setName(); 设置线程的名称
  • currentThread(); 获取当前的线程的对象

四.线程安全

4.1 线程安全是什么, 发生的原因

多线程同时操作同一个资源的时候
可能会出现业务安全问题, 称之为线程安全

出现的原因:
存在多个线程并发
并同时访问共享的资源
同时修改共享资源

4.2 案例模拟

例如
有两个人有一个共享的账户
里面有100000块钱
A和B同时想取出100000快
在这里插入图片描述

五.线程同步

解决以上的安全问题
让并发的多线程访问同一个共享资源时, 要排队
例如一堆人抢同一个商品
肯定是并发的
但是,能不能抢到就看哪一个线程更快执行
最快的线程能看到商品的库存为1
其他的线程就不能看该商品的库存了, 等看到时,库存已经为0

实现方法
对共享的资源进行加锁
每一次只能一个线程访问, 其他线程必须在外面等待
解锁之后才能访问
锁对象每一次只能一个线程拥有
同时抢锁对象的线程
会有一套分配锁对象的算法
锁对象唯一

加锁的三种方法:

5.1 同步代码块

作用: 把出现线程安全问题的核心代码给上锁
原理: 每一次都有且仅有一个线程能执行这一块代码

synchronized(同步锁对象){    //同步锁对象: 例如 "itdong" 字符串对象就是唯一的
	操作共享资源的代码
}

快捷键:
选中代码块
按住ctrl+alt+T

锁对象用任意唯一的对象好不好呢?
不好, 会影响其他无关线程的执行
例如:
小明小红的共同账户是A, 小黑小白的共同账户是B
小明来取钱,小红不能取是可以的, 但是小黑或者小白也不能取就是Bug

建议使用共享的资源作为锁对象
所以对于实例方法, 每一个线程类的实例在被创建时, 都应该传入共享资源作为属性, 然后使用this作为锁对象
而对于静态方法, 使用类对象(类名.class)

5.2 同步方法

作用: 把出现线程安全问题的核心方法给上锁
和同步代码块是一样的道理

同步方法的底层原理, 同步方法其实也是有隐式锁对象的, 只是锁的范围是一整个方法
如果方法是实例方法, 同步方法的锁对象默认就是this, 但是代码要高度面向对象
如果是静态方法,默认是类对象作为锁对象.

5.3 Lock锁

一种更加灵活的方式, 这个锁对象, 拥有更加丰富的API可以来控制锁对象
使用方法:
只需要在共享资源中定义这个锁对象
private final Lock lock = new ReentrantLock();
常用的API如
lock.lock();
lock.unlock();

六.线程通信

线程之间相互的发送数据
通过一个共享的数据来实现
跟据这个共享的数据决定自己该怎么做, 或者通知其他线程该怎么做
例如一个经典的模型
生产者和消费者模型
生产者负责生产数据
消费者负责消费数据
共享的资源(产品)决定这这两种线程将这么行动
例如: 生产者认为产能不能过剩,当产品没有被消费时,不能继续生产, 自己等待自己
而消费者只有当存在产品时才能进行工作, 当没有产品时, 消费者就要等待者生产者产出产品

不难看出, 线程通信是载线程同步的基础之上建立的技术, 本质上也是共享资源只能被一个线程来操作, 不同是, 共享资源还决定者线程自己本身的行动并去控制其他线程.

不得不提的就是线程唤醒和等待这两个API
void wait();
void notify();
void notifyall();
值得注意的是
以上方法是Object类, 理论上所有的类都能调用
推荐使用同步锁对象进行调用
锁对象可以直接知道具体是哪一个线程要等待, 并将自己解锁.

//创建一个手机对象
public class Phone {

    private boolean flag = false;  //默认是无来电状态

    //手机运行时的主线程
    public static void main(String[] args) {
        Phone huawei = new Phone();
        huawei.starting(); //开机啦
    }

    //表示手机开机开始工作
    public void starting(){

        //有两个线程, 分别负责手机的接听电话和提示来电
        //生产者
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {//明显这里的锁对象就是华为手机这个实例, 注意: 锁一定要是一个对象来充当
                    synchronized (Phone.this) {  //如果这里this, 指的是这个匿名内部类, 内部类拿到外部类的对象就是外部类名加this, 谁调用的方法, this就指向哪一个实例
                        while(true){
                            if (flag){
                                //说明有来电了, 唤醒消费者, 等待自己
                                System.out.println("有来电了, 请接听...........");
                                Phone.this.notify();
                                Phone.this.wait();
                            }else{
                                //说明没有来电
                                System.out.println("监听来电中..........");
                                flag = true;
                            }
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"监听来电").start();

        //消费者
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    synchronized (Phone.this) {
                        while(true){
                            if (flag){
                                //说明有来电了
                                System.out.println("接通.........正在通话");
                                Thread.sleep(5000);
                                flag = false;
                                System.out.println("通话结束");
                            }else{
                                Phone.this.notify();
                                Phone.this.wait();
                            }
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"接听线程").start();
    }
}

七.线程池概述、线程池的七个参数详解

7.1线程池概述

线程池就是一个可以复用线程的技术
在这里插入图片描述

7.2线程池实现的API, 参数说明

jdk5.0之后, java提供了一个实现了ExecutorService接口的实现类ThreadPoolExecutor来代表线程池
在这里插入图片描述
在这里插入图片描述
注意: 有线程能处理直接用线程, 没有线程能处理才进任务队列.

ThreadPoolExecutor创建线程池对象实例:

ExecutorService pools = new ThreadPoolExecutor(
3,
5,
8,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(6),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);

ExecutorService的常用方法

void execute(Runnable command)  --执行任务/命令,没有返回值,一般用来执行Runnable任务。
Future<T> submit(Callable<T> task) --执行任务,返回任务对象的执行结果,一般用来执行Callable任务。
void shutdown() --等任务执行完毕之后关闭线程池
List<Runnable> shutdownNow()  --立刻关闭/停止正在执行的任务并返回队列中未执行的任务。

在这里插入图片描述

7.3线程池处理Runnable任务

很明显,这里主要是使用的这个方法
void execute(Runnable command) --执行任务/命令,没有返回值,一般用来执行Runnable任务。
那么首先就要获取获取Runnable对象。

7.4 线程池处理Callable任务

很明显,这里使用的是这个方法
Future<T> submit(Callable<T> task) --执行任务,返回任务对象的执行结果,一般用来执行Callable任务。
同样的需要很多的Callable对象。

7.5Executors工具类实现线程池

除了以上的创建方法, 线程池还能通过一种方法来创建。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

八. 线程的并发并行、声命周期

正在运行的程序(软件)就是一个独立的进程,而线程是属于进程的,多个线程其实是并发和并行执行的。
cpu能够同时处理的线程数量是有限的。
cpu会轮巡为系统的每一个线程服务,由于cpu的切换速度太快了,给我们的感觉就是这些线程在同时进行。这就是并发。

而并行就是多个线程实实在在的同时在cpu中执行。
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值