多线程以及线程池

一个Java程序最少有两个线程:一个是main主线程,一个是GC垃圾回收机制守护线程

线程的实现

两个方法:

方法一:一是继承Thread,重写run方法

class MyThread extends Thread {
    @Override
    public void run() {
        线程需要执行的代码
    }
}

public static void main(String[] args) {
    // 实例化自定义继承 Thread 类子类对象
    MyThread1 mt = new MyThread();  
    // 利用继承父类 Thread 中的 start 方法,启动线程。【切记不可以直接调用 run 方法】
    mt.start();
}

方法二:二是实现Runnable接口,实现run方法

class MyThread implements Runnable {
    @Override
    public void run() {
        线程需要执行的代码
    }
}

main() {
   //向Thread类中传入MyThread对象,通过Thread对象调用Start方法启动线程。
    Thread t1 = new Thread(new MyThread());
    
    t1.start();
}

线程的主要方法

Thread构造方法:
  Thread();无参构造方法
  Thread(Runnable对象);//传入Runnable对象参数,主要是供实现Runnable接口的类提供的
  Thread(Runnable对象, 线程名字);//同上,而且可以给线程另起新名字

Thread成员方法:
  String getName();
  void setName(String name);//设置线程名     
    
  int getPriority();
  void setPriority(int newPriority);//设置线程优先级
            //1. 线程优先级 1 ~ 10 默认 5
            //2. 线程优先级无法决定线程的执行顺序,有且只能增加线程的执行概率
    
  void setDaemon(boolean flag);
  boolean isDaemon();//设置和判断当前线程是否为守护线程
        //flag 为 true 表示守护线程

Thread静态成员方法:
  public static void sleep(int ms) throws InterruptedException;
        //在哪一个线程代码中执行,哪一个线程代码进入【定时睡眠阻塞】状态
        //在睡眠状态无法被叫醒,只有到时间才会抢锁
  public static Thread currentThread(); 
        //在哪一个线程代码中执行,获取当前线程代码对应的线程对象。

Object提供的服务线程的方法
  void wait();//无限等待,但随时可以被叫醒
  void wait(long timeout);//有等待时长,但随时可以被叫醒
  notify();//随机唤醒一个等待线程
  notifyAll();//唤醒所有等待线程

线程同步【☆】

线程的同步主要是使用synchronized关键字来限制线程在同一时间只有一个线程访问被synchronized修饰的代码块或方法

synchronized关键字使用方法

方法一:同步代码块

synchronized (/* 锁对象 */) {
    // 限制同步的代码
}
同步代码块中在同一时间有且只有一个线程能访问,线程访问时持有锁,在线程执行完任务后,释放锁
锁必须是一个【对象】,且同一种线程类只能使用同一把锁,否则线程还是不安全的

方法二:同步方法

非静态同步方法
public synchronized zyMethod(){
// 限制同步的代码
}

静态同步方法
public static synchronized zyMethod(){
// 限制同步的代码
}

静态和非静态同步方法的区别在于锁对象不同,现在我们讲怎么样选择锁,这个非常重要

锁的选择细节【☆☆☆☆☆】

锁是为了让线程们在执行同一个任务的时候,不同时对数据进行操作,保证一个线程执行本任务时另一个想要执行本任务的线程在外面排队,等我执行完,释放锁你才能进,这样才能保证线程安全,不会出现脏读,幻读,不可重复读现象。

在多线程这一块,我们选取的锁对象一般都需要全局唯一的,所以我们经常使用静态final对象,来当锁

static final Object obj = new Object();//此对象能保证整个java项目运行中锁都是唯一的
synchronized (/* 锁对象 */) {
    // 限制同步的代码
}
非静态同步方法中的锁对象为 this ,也就是调用这个方法的对象
,但new这个类的操作基本不可能唯一,所以锁对象也不唯一,不安全
public synchronized zyMethod(){
// 限制同步的代码
}
静态同步方法的锁对象是 类名.class ,字节码文件是全局唯一的。
public static synchronized zyMethod(){
// 限制同步的代码
}

线程池

线程池的概念是在线程的基础上提出的,我们在使用线程的时候,每次执行线程任务,都需要创建新的线程对象,执行完线程任务后,立即销毁,很浪费资源,这时我们提出,我们生成的线程对象在使用的时候过来执行任务,在执行完任务或没有任务时,线程进入等待,等待新任务的到来,这样既能保证有空闲线程来做任务,有能让资源不浪费在创建和销毁线程上

java线程池核心的实现类是ThreadPoolExecutor,通过该类的构造方法来创建线程池

public ThreadPoolExecutor(int corePoolSize,//核心线程数量,决定是否需要添加新的线程来执行任务
                          int maximumPoolSize,//最大线程数量,线程池中允许创建线程地最大数量
                          long keepAliveTime,//线程空闲时存活的时间
                          TimeUnit unit,//空闲存活时间单位
                          BlockingQueue<Runnable> workQueue,//任务队列,用于存放已提交的任务
                          ThreadFactory threadFactory,//线程工厂,用于创建线程执行任务
                          RejectedExecutionHandler handler) //拒绝策略,当线程池处于饱和时,使用某种策略来拒绝任务提交

线程池是如何动态添加和销毁线程的

1.如果 task < corePoolSize 则创建线程执行提交的任务

2.如果 task >= corePoolSize 同时 阻塞队列未满,则将任务添加至阻塞队列,等待后续线程来执行提交地任务

3.如果task >= corePoolSize 同时 task < maxinumPoolSize , 阻塞队列已满,则创建非核心线程执行提交的任务,非核心线程在执行完任务会等待任务分配,如果等待超时,则会销毁非核心线程

4.如果task >= maxinumPoolSize 同时阻塞队列已满, 则执行拒绝策略

ps:线程池创建的时候强烈建议使用ThreadPoolExecutor的构造方法

Executors 返回的线程池对象的弊端如下:

1) Excutors.fixedThreadPool(fixedPoolSize)和 SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。

2) Excutors.cachedThreadPool() 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

如有不足,私信或者评论指正,肥肠感激。🙋

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值