java- 多线程知识点梳理

本文深入探讨Java多线程的概念,包括线程与进程的区别,同步与异步、并发与并行的特性,以及线程的创建和管理。详细解析Thread类和Runnable接口的使用,线程安全问题及解决方案,线程池的原理与应用,Callable接口的异同,以及线程死锁的避免策略。

多线程

线程与进程

进程

是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间

线程

是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行. 一个进程最少
有一个线程
线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分
成若干个线程

同步与异步

同步:排队执行 , 效率低但是安全.
异步:同时执行 , 效率高但是数据不安全.

并发与并行

并发:指两个或多个事件在同一个时间段内发生。
并行:指两个或多个事件在同一时刻发生(同时发生)。

Thread

public class MyThread extends Thread {
//run方法就是线程要执行的任务方法

@Override
public void run() {
    //这里的代码就是一条新的执行路径
    //这个执行路径是触发方式,不是调用run方法,而是通过thread对象的start方法来启动任务
    for (int i = 0; i < 10; i++) {
        System.out.println("锄禾日当午"+i);
    }
}
}

Runnable

实现Runnable 与 继承Thread相比有如下优势:

1.通过创建任务,然后给线程分配的方式来实现的多线程,更适合多个线程同时执行相同任务的情况。

2.可以避免单继承所带来的局限性。

3.任务与线程 本身是分离的,提高了程序的健壮性。

4.线程池技术,接收Runnable类型的任务,不接收Thread类型的线程。

//实现Runnable`
//1. 	创建一个任务对象	
MyRunnable r = new MyRunnable();
//2.	创建一个线程,并为其分配一个任务
Thread t = new Thread(r);
//3.	执行这个线程
t.start();`
 for (int i = 0; i < 10; i++) {
        System.out.println("锄禾日当午"+i);
    }

Thread类

常用方法

getname() 获取线程的名称

getId() 获取线程的标识符

setPriority(int newPriority)` 更改线程的优先级

currentThread()` 表示当前正在执行的线程

start() 线程启动

interrupt()` 线程中断

sleep(long millis)`/sleep(long millis(毫秒), int nanos(纳秒)) 暂时停止线程执行(指定时间)

setDaemon(boolean on)` 标记线程是否为守护线程或用户线程

守护线程与用户线程

Daemon线程(守护线程)

​ ↓(依附于 即人在塔在的关系)

用户线程(全部死亡,程序结束)

    //用户线程:当一个进程不包含任何的存活的用户线程时,进行结束
    //守护线程:守护用户线程的,当最后一个用户线程结束时,所有守护线程自动死亡。

设置和获取线程的名称

public static void main(String[] args) {
    //如何获取线程的名称
    System.out.println(Thread.currentThread().getName());
    //两种设置线程名称的方式
    Thread t = new Thread(new MyRunnable());
    t.setName("wwww");
    t.start();
    new Thread(new MyRunnable(),"锄禾日当午").start();
    //不设置的有默认的名字
    new Thread(new MyRunnable()).start();
}
static class MyRunnable implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

线程的休眠

Thread.sleep(1000); //1000毫秒

线程不安全

解决方案1、同步代码块(synchronized)

格式 synchronized(){}

​ ↑(传参:锁对象)

解决方案2、同步方法(synchronized关键词修饰方法)

举例:public synchronized boolean sale(){}

解决方案3、显示锁,Lock ,子类 ReentrantLock

自己创建一把锁,自己上锁,自己解锁

 static class Ticket implements Runnable{
        //总票数
        private int count = 10;
        //参数为true表示公平锁    默认是false 不是公平锁
        private Lock l = new ReentrantLock(true);
        @Override
        public void run() {
            while (true) {
                l.lock();//当线程开始执行时,进行上锁
                    if (count > 0) {
                        //卖票
                        System.out.println("正在准备卖票");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        count--;
                        System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
                    }else {
                        break;
                    }
                    l.unlock();//线程结束,解锁
            }
        }
    }

公平锁:先到先得,排队流程。

不公平锁:先抢先得,手快有,手慢无。

线程死锁

个人理解:

前提:锁1调用锁2。

发生死锁:锁1线程执行一半,需要调用锁2线程方法,于是申请调用锁2,但是与此同时锁2线程也执行到一半,需要调用锁1线程方法。于是就发生了死锁。即1在等待2结束然后调用2的方法结束自己,同时2在等待1的结束然后调用1的方法结束自己。

前提:小明借了小黑一把指甲刀

小黑:小明借我一下“你的指甲刀”

小明:我的指甲刀在你那啊。

小黑:那你给我你的指甲刀啊。

小明:你不还我指甲刀,我怎么借你指甲刀啊。

小黑:你不借我指甲刀,我怎么还你啊。

八十年后,小明小黑卒。

如何避免:在任何一个有可能产生锁的方法里,不要调用另一个有可能产生锁的方法。

Callable

Runnable 与 Callable的相同点

都是接口
都可以编写多线程程序
都采用Thread.start()启动线程

Runnable 与 Callable的不同点

Runnable没有返回值;Callable可以返回执行结果
Callable接口的call()允许抛出异常;Runnable的run()不能抛出

接口定义
//Callable接口
public interface Callable<V> {
 V call() throws Exception;
}
//Runnable接口
public interface Runnable {
 public abstract void run();
}

Callable获取返回值

Callalble接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执
行,如果不调用不会阻塞。

Callable使用步骤

1. 编写类实现Callable接口 , 实现call方法
   class XXX implements Callable<T> {
   @Override
     public <T> call() throws Exception {
       return T;
     }
   }
2. 创建FutureTask对象 , 并传入第一步编写的Callable类对象
   FutureTask<Integer> future = new FutureTask<>(callable);
3. 通过Thread,启动线程
   new Thread(future).start();

常用方法

get() 获取线程执行的结果。

isDone() 判断线程是否执行完毕。

线程池 Executors

如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程
就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间. 线程池就是一个容纳多个线程的容
器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。

线程池的好处

降低资源消耗。
提高响应速度。
提高线程的可管理性。

Java中的四种线程池 . ExecutorService

1.缓存线程池
//缓存线程池
//无限制长度
//任务加入后的执行流程
//1判断线程池是否存在空闲线程  
  2存在则使用   
  3不存在则创建线程并使用
//向线程池中加入新的任务
2.定长线程池
长度是指定的线程池
加入任务后的执行流程
  1 判断线程池是否存在空闲线程
  2 存在则使用
  3 不存在空闲线程  且线程池未满的情况下  则创建线程  并放入线程池中  然后使用
  4 不存在空闲线程  且线程池已满的情况下  则等待线程池的空闲线程
3.单线程线程池
单线程线程池
执行流程
        1 判断线程池的那个线程是否空闲
        2 空闲则使用
        3 不空闲则等待它空闲后再使用
4.周期任务 定长线程池
执行流程
        1 判断线程池是否存在空闲线程
        2 存在则使用
        3 不存在空闲线程  且线程池未满的情况下  则创建线程  并放入线程池中  然后使用
        4 不存在空闲线程  且线程池已满的情况下  则等待线程池的空闲线程
周期性任务执行时
定时执行 当某个任务触发时  自动执行某任务
public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
        //定时执行一次
        //参数1:定时执行的任务
        //参数2:时长数字
        //参数3:2的时间单位    Timeunit的常量指定
       scheduledExecutorService.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        },5, TimeUnit.SECONDS);      //5秒钟后执行*/
        周期性执行任务
            参数1:任务
            参数2:延迟时长数字(第一次在执行上面时间以后)
            参数3:周期时长数字(没隔多久执行一次)
            参数4:时长数字的单位
        * **/
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        },5,1,TimeUnit.SECONDS);
    }
}
以上就是我经过学习和请教后梳理的多线程知识。如有重复,纯属水平有限[捂脸]。

本人只是java学习中的小学生,有更好的想法请说明并解释,不然我怕我看不懂[捂脸]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值