初步认识多线程以及线程池

1.线程简介

进程:进程是活动的程序,已经启动进驻到内存中正在使用的程序,每个进程拥有独立的内存空间。

线程:线程是进程中最小的调度单元,cpu控制的最小的执行单元。

任何一个程序都至少有一个线程在使用,多个线程共享内存。多线程切换消耗的资源少。

2.并发与并行

并发:在同一时间间隔内,同时有多个线程运行。如果一台主机只有一个cpu,那么在某一时刻内最多只能有一个线程在cpu中运行,如果有多个程序需要运行,则将一个时间间隔分成小的时间段,在每个时间段中执行不同的线程。   

并行:在同一时刻,同时有多个线程运行。如果一台主机有两个cpu时候,如果有多个线程需要执行,则分别在两个cpu上分时间段运行。

3.如何创建线程

        3.1.继承Thread类

                要重写run方法

public class MyThread1 extends Thread{
    @Override
    public void run() {
        super.run();
    }
}

               创建线程对象,启动线程

public class Test {
    public static void main(String[] args) {
        MyThread1 myThread1=new MyThread1();
        myThread1.start();
    }
}

        3.2.实现Runnable接口

                重写run方法

public class MyRun implements Runnable{
    @Override
    public void run() {
        ...
    }
}

               

public class Test {
    public static void main(String[] args) {
        MyRun myRun=new MyRun();
        Thread thread=new Thread(myRun);
        thread.start();
    }
}

        3.3.实现Callable接口

        可以获取线程的返回值

public class MyCall implements Callable<String> {
    @Override
    public String call() throws Exception {
        System.out.println("线程在执行的任务");
        return Thread.currentThread().getName();
    }

    public static void main(String[] args) {
//        创建Callable接口的实例
        MyCall myCall=new MyCall();
//       FutureTask 实例
        FutureTask<String> task=new FutureTask<>(myCall);
        Thread thread=new Thread(task);
        thread.start();
    }
}

小结:

1、创建线程的三种方式:

  1. 继承Thread类

  2. 实现Runnable接口

  3. 实现Callable接口,配套使用FutureTask进行转换

2、什么时候用哪种方式?

  • 如果有返回结果:Callable

  • 如果没有返回结果:继承Thread类|实现Runnable接口

  • 继承只能单继承,多个线程共享数据使用Runnable接口

方法描述
public static Thread currentThread()返回当前的线程
public final String getName()返回线程名称
public final void setPriority(int priority)设置线程优先级
public void start()开始执行线程
public static void sleep(long m)使目前的线程休眠m毫秒
public final void yield()暂停目前的线程,运行其他 线程
public void run()线程要执行的任务

4.线程状态

1、新建状态(New):新创建了一个线程对象。

2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。

阻塞的情况分三种:

(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)

(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。

(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)

5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

5.线程同步

线程同步:即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作。那相对的线程异步处理就不用阻塞当前线程,而是允许后续操作,直至其他线程将处理完成,并回调此线程。

线程同步的利弊:

好处:解决了线程同步的数据安全问题

弊端:当线程很多的时候,每个线程都会去判断同步上面的这个锁,很耗费资源,降低效率。

实现同步的方式:

1、基于synchronized(同步)实现

synchronized关键字可以用于修饰方法、代码块或静态方法。当修饰方法时,synchronized关键字将锁定整个方法,而当修饰代码块时,只会锁定代码块中的部分代码。静态方法的锁定范围是整个类。

2.基于Lock实现

ReentrantLock类实现了Lock,它拥有和synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显示加锁,释放锁。

private Lock lock = new ReentrantLock();//定义Lock类型的锁  
public void method(int a){
        lock.lock();//上锁
        try {
            。。。
        }finally {
            lock.unlock();//解锁
        }
    }

synchronized与Lock的对比

Lock是显示锁,需要自己手动开启和关闭,synchronized是隐式锁,出了作用于自动释放,无需自己释放。

Lock只能锁代码块,synchronized可以锁代码块和方法

使用Lock锁,JVM将花费更少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)

6.死锁

死锁:当多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,从而导致两个或者多个线程都在等待对方释放资源,都停止执行的情况。尽量不要让 一个同步代码块 同时拥有两个以上的对象的锁

7.Object类中对线程的支持

方法描述
public final void wait()线程等待 ,释放锁 ,用在同步方法中的。
public final void wait(long timeout)线程等待,并指定等待时间,以毫秒为单位
Public void notify()唤醒一个等待的线程
Public void notifyAll()唤醒全部等待的线程

sleep方法和wait方法的区别?

1、sleep是Thread类种的方法,wait是Object中的方法。

2、wait必须用在同步代码中,释放锁,让当前线程处于等待状态;sleep不会解锁;

8.线程池

线程池就是首先创建一些线程,他们的集合称之为线程池。线程池在系统启动时会创建大量空闲线程,程序将一个任务传递给线程池,线程池就会启动一条线程来执行这个任务,执行结束后线程不会销毁(死亡),而是再次返回到线程池中成为空闲状态,等待执行下一个任务。

线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后再需要执行新的任务时重用这些线程而不是新建线程

多线程运行时,系统不断创建和销毁新的线程,成本非常高,会过度的消耗系统资源,从而可能导致系统资源崩溃,使用线程池就是最好的选择

8.1创建线程池

 public ThreadPoolExecutor(int corePoolSize,
                           int maximumPoolSize,
                           long keepAliveTime,
                           TimeUnit unit,
                           BlockingQueue<Runnable> workQueue,
                           RejectedExecutionHandler handler) 
  • corePoolSize:线程池核心线程数量

  • maximumPoolSize:线程池最大线程数量

  • keepAliverTime:当活跃线程数大于核心线程数时,空闲的多余线程最大存活时间

  • unit:存活时间的单位

  • workQueue:存放任务的队列

  • handler:超出线程范围和队列容量的任务的处理程序

8.2线程池的工作流程

1、提交任务,判断核心线程是否已满,如果未满,创建核心线程用来处理任务;如果已满进入下一步

2、判断阻塞队列是否已满,如果未满,将任务放入队列中,等待执行,如果已满,进入下一步

3、如果线程池中的最大线程未满,创建线程执行任务,如果已满,按照拒绝策略进行处理。

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值