(java)线程&线程池

1. 线程

  • 线程、进程概念:

进程可以理解为内存空间,进程是系统进行资源分配和调度的基本单位。

线程就是真正处理任务的,线程是进程中的一个执行单元,它负责执行进程中的代码。

  • 并发、并行概念:

并发:单位时间片内,只执行一个任务(java就是如此)

并行:单位时间片内,同时执行多个任务


  • 如何在java中创建一个线程?(我们可能更常用的就是第二种方法的匿名内部类)

方法一:继承Thread父类

  1. 创建Thread 子类,继承父类

  2. 重写其父类run方法

  3. 创建子类实例

  4. 执行子类start方法

方法二:实现Runnable接口

  1. 创建一个Runnable接口的实现类
  2. 重写接口的run方法
  3. 创建实现类的实例
  4. 创建Thread对象,且将实现类实例作为参数传入,调用其start方法

  • 线程的方法

setName/getNmae:获取线程名字或设置线程名字

Thread.sleep(long millis);设置当前线程休眠毫秒数

Thread.currentThread();获取当前线程对象,弥补使用实现Runnable接口无法直接获取线程对象的弊端

strat();开启线程

补充:可以在声明线程时给定名字:new Thread(sellTicket, "天猫").start();sellTicket是runnabe的实现类,名字可以在声明时直接给定


  • 如何理解java多线程/为什么多线程可以并发处理多个任务?

    答:我们每创建一个线程,都会开辟一个新的栈空间,而CPU可以在不同栈顶交替并发执行多个任务,我们在学习之前线程之前是依靠JVM先执行main方法,为我们开辟主线程。

//补充:再次加深对于面向对象的理解:
//我们通过匿名内部类的方式,分别创建Runnable的实现类r和Thread的子类,此时把r放在Thread的子类的参数位置,请问此时我们输出结果是调用Runnable的实现类r的run方法 还是 Thread的子类的run方法?
//题目解析:
//其实本质上还是在运行Thread匿名子类的方法,接口对象的实现的前提是在调用接口的

       Runnable r =  new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 105; i++) {
                    System.out.println("第二种匿名内部类"+ i);
                }
            }
        };//通过匿名内部类的方式获取实现Runnable接口的实现类
       new  Thread(r){
           @Override
           public void run() {
               //super.run();
               for (int i = 0; i < 100; i++) {
                   System.out.println("第一种匿名内部类");
               }
           }
       }.start();//通过匿名内部类的方式,直接获取Thread的子类

2. 线程同步

多线程操作,会有什么问题?当多个线程同时操作公共数据时,可能会导致程序出错

为了解决多线程操作可能出现的问题,我们可以使用三种方法来为程序上锁,当上锁代码在被一条线程执行时,其他线程无法同时执行该段代码

2.1 同步代码块(推荐)

语法格式:synchronized(锁对象){需要被同步的代码 }

直接上代码更好理解:

//三种的原理相同,就是给特定代码上锁,要求学会同步代码块

//以买票举例(演示同步代码块):
//卖票 SellTicket类 继承Runnable接口,重写run方法,使用synchronize同步代码块包裹需要上锁的代码
package cn.itcast.practice.synchronize;
public class SellTicket implements Runnable {
    private int count = 100;//一共100张票
    private static final Object lock = new Object();//制造锁对象
    @Override
    public void run() {
        while (true) {
            synchronized (lock) {
                if (count == 0) {
                    break;
                }//一定要把判断条件也包裹在代码块中
                System.out.println(Thread.currentThread().getName() + "正在买" + count + "张票");
                count--;
            }
        }
    }
}

//另一个类中,建立三条线程,共同操作一个SellTicket实例,一起卖这100张票
package cn.itcast.practice.synchronize;
public class TicketTest {
    public static void main(String[] args) {
        SellTicket sellTicket = new SellTicket();
         //构造方法 new Thread(Runnable target,String name)
        new Thread(sellTicket, "天猫").start();
        new Thread(sellTicket, "京东").start();
        new Thread(sellTicket, "淘宝").start();
    }//main
}

2.2 同步方法

语法格式:修饰符 synchronized 返回值类型 方法名(参数列表){}

缺点:

  • 无法自定义锁,直接给默认锁,

    1. 静态方法锁为当前类的字节码的对象:例如字符串的字节码对象就是 String.class
    2. 非静态方法锁位当前的实例,也就是this
  • 方法中所有代码被锁,无法锁住方法中特定的一部分

补充:因为同步方法的缺点,导致使用了同步方法的一批类都消失了,例如Vector是Arraylist的前身,Hashtable是HashMap的前身,对于需要多线程操作时,对于例如:Arraylist和HashMap,可以使用集合工具类为我们提供的Synchronize方法

//同步版本的和未同步版本的是一母同胞,变化结果相同。
        ArrayList<Integer> list = new ArrayList<>();
        //使用Collections工具类,把可能会多线程安全出问题的list转变为更安全的list1
        List<Integer> list1 = Collections.synchronizedList(list);
        list1.add(111);//给list1添加元素
        System.out.println(list);//list结果也会变为 [111]

2.3 Lock锁对象

如果不愿意使用自定义锁,可以使用官方为我们提供的锁对象

语法:Lock lock = new ReentrantLock()

使用方式:lock.lock();手动上锁 lock.unlock();手动开锁

注意事项:无论前面代码是否正常执行,一定要保证开锁,所以可以用try-finally包裹,所以比较麻烦。

//还是用前面老师的例子,简单了解一下即可
public class SellTicket implements Runnable {
    private int ticket = 100;
    private Lock lock = new ReentrantLock();//使用Lock的子类ReentrantLock创建锁实例
    @Override
    public void run() {
        while (true) {
            try{
                lock.lock();
                if (ticket <= 0) {
                    break;
                }
                ticket--;
                System.out.println(Thread.currentThread().getName() + "卖出一张票,还剩下" + ticket + "张");
            }finally {
                lock.unlock();
            }
        }
    }
}

3. 线程池

定义:存放线程的池子, 线程池中的线程,执行完毕后,不销毁而是归还到池子中,继续服务下一个任务,提高线程的使用率,其解决了频繁创建线程以及销毁线程的动作, 节约时间和资源。

线程池对象:【面试题:线程池七大核心参数

    ThreadPoolExecutor(
        int corePoolSize, //核心线程池数量
        int maximumPoolSize,// 最大线程池数量, 扩容的线程我们称为临时线程
        long keepAliveTime, // 临时线程空闲的时间,就会销毁
        TimeUnit unit, // 临时线程空闲的时间单位,就会销毁
        BlockingQueue<Runnable> workQueue, // 存放任务的队列
        ThreadFactory threadFactory, //创建线程的方式
        RejectedExecutionHandler handler// 拒绝策略
    )
        
//补充拒绝策略的四大参数:
//new ThreadPoolExecutor.AbortPolicy(): 直接拒绝, 并且抛出异常(推荐,且为默认值)
//new ThreadPoolExecutor.DiscardPolicy(); 直接拒绝, 不抛异常
//new ThreadPoolExecutor.DiscardOldestPolicy(); 丢弃队列中最老的任务,再执行
//new ThreadPoolExecutor.CallerRunsPolicy(); 不拒绝,自己处理丢给主线程!!!
  • 线程触发扩容临时线程的条件: 当任务量> 核心线程数量+队列 就会扩容!!!

  • 什么时候会触发拒绝策略: 当任务量 > 最大线程 + 队列,就会触发拒绝策略

线程池方法:为线程池对象提交线程,语法;线程池实例.submit(线程对象)

//线程池案例;
 ThreadPoolExecutor tpe = new ThreadPoolExecutor(
                3,
                5,
                30,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(10),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy() );//线程池的7大参数

        for (int i = 0; i < 20; i++) {
            tpe.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "正在服务我");
                }
            });
        }
//案例解析:
//核心线程数量为3,最大线程数量为5,存放任务队列为 10
//如果提交13个以下的任务,任务队列不满,则只有核心线程工作
//如果提交13个以上的任务,所有线程都在工作,
//如果提交的超过了15个,则会触发拒绝策略

补充: 线程池也有自己直接的API:(不太实用,不如自己造线程池)

Executors.newCachedThreadPool(); 不好, 没有上限的线程池!!!
Executors.newFixedThreadPool(int max);不好, 有上限的线程池, 最多是你给的最大限制


补充:线程的状态(配合图解进行学习)
在这里插入图片描述

线程的状态:表达含义:
NEW被创建却并没有被start,尚未被启动
RUNNABLE已经被start,可以被CPU执行的程序
BLOCKEDCPU正在运行此线程,但是遇到锁,进不去的状态
WAITING无限等待状态, 线程遇到wait方法, 处于无限等待, 必须通过notify或者notifyAll才能唤醒
TIMED_WAITING计时等待, 线程遇到sleep(long time); 当时间够了会自动唤醒
TERMINATED销毁状态, 线程执行完后的状态
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值