多线程和线程池

前言

1、线程不是由Java创建的,是由底层C++创建的。
2、线程是CPU调度的最小单元,进程是CPU分配资源的最小单元
3、资料整理参考自B站传智播客

一、关于线程和进程

1、概念:进程就是相当于一个应用程序,比如说是英雄联盟
而在联盟游戏,又有很多的线程,比如:英雄皮肤和动作,技能,英雄属性等等都是由线程监控的。
2、内存数据:进程之间没有直接关系,进程中的线程可以共享堆区数据
3、并发与并行:一般电脑上都是只有一个CPU的,一个CPU在同一时间点只能执行一个线程,但是通过虚拟化技术,可以同时处理多个线程,由于处理速度很快,在我们肉眼可见的1s中,可能已经执行了很多个线程
并发:多个线程在同一时间段内交替运行,由于CPU处理速度很快,在宏观上可以认为是’并行’的
并行:多个线程在同一时间点同时执行
4、生命周期
在这里插入图片描述
5、Java中线程对共享变量的操作
在这里插入图片描述

6、并发编程(JUC)的三大特性
①原子性:一个操作或多个操作,要么在全部执行并且在执行过程中不被打断,要么全部不执行。
②可见性:由于线程使用的是当前工作的副本,当修改时才会同步到主内存中,但是如果别的线程修改了主内存的东西,那么其他线程是不知道的,volatile,防止指令重排(避免调整我们的代码顺序),可见性(使用volatile修饰的变量在被一个线程修改后,虚拟机会强制刷新到主内存中,并且强制要求其他用到这个变量的线程读取这个修改后的变量)(这个和门票的那个想悖,门票可以查看变化,而这个flag却不能,我的理解是在修改变量是才会读取主内存到自己工作副本中,如果如果只是使用就只会读取自己的工作副本
在这里插入图片描述
③有序性:在保证了原子性的情况下,其实有序性显得不是很重要了,保证代码执行顺序按照我们写的代码先后顺序执行,不让CPU进行指令重排

7、synchronized、wait、notfiy
这里有必要提一下,
①synchronized是一种锁机制
②wait是指让活跃在当前对象的上线程(也就是当前线程,因为当前执行代码就是当前自己的这个线程)进入waiting状态(一种阻塞状态,必须有notify/notifyAll唤醒),并且会释放当前对象的锁
③notify,随机唤醒一个线程,notify唤醒所有线程
④wait和notify必须出现同步代码块中,也就是锁中,如此才能保证锁住对象并且由notify释放,进入唤醒线程唤醒其他线程。
个人理解:在一个同步代码块中,让线程更好的通信,如果一个线程在同步代码块中发现自己不需要进行操作或者不满足执行操作的条件,那么自己主动进入阻塞状态(sleep也可以,但是无法释放锁),释放锁,让其他线程执行(唤醒)

二、线程中常用API

1.创建线程

1)Callable:有返回值

        FutureTask<Object> task = new FutureTask(()->new Object());
        Thread thread = new Thread(task);
        thread.start();
        System.out.println(task.get());

2.守护线程(daemon)

当所有的用户线程结束之后,守护线程也就没有必要了,整个程序就会终止

import jdk.nashorn.internal.runtime.Context;

/*
正常情况下,t2肯定先打印玩,随后t1打印完成进程关闭
如果t1变成守护线程,那么用户线程只有两个,主线开启两个线程之后就结束了,然后t2结束之后,t1停止打印
 */
public class Daemon {
    public static void main(String[] args) {
        Thread t1 = new Thread(){
            public void run(){
                for(int i = 0 ; i< 100 ; i++){
                    System.out.println(Thread.currentThread().getName()+"===>"+i);
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
             }
        };
        Thread t2 = new Thread(){
            public void run(){
                for(int i = 0 ; i< 100 ; i++){
                    System.out.println(Thread.currentThread().getName()+"===>"+i);
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        t1.setName("t1");
        t2.setName("t2");
        t1.setDaemon(true);//成为守护线程
        t1.start();
        t2.start();
    }
}

3.线程终止(interrupt)

使用interrupted()方法终止,在线程中监听interrupted状态,如果为true说明外界要求当前线程停止,在保存相关信息再停止,如果线程睡眠,睡眠会触发异常,此时interrupted并不是true

public class Stop {
    public static void main(String[] args) {
        Thread t1 = new Thread(){
            public void run(){
                while(true){
                    //使用sleep会出错
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        //睡眠被打断
                        this.interrupt();
                    }
                    boolean interrupted = Thread.currentThread().isInterrupted();//是否中断
                    if(interrupted) {System.out.println("线程已中断,停止运行");break;}
                    else System.out.println("线程正在实行");
                }
            }
        };
        t1.start();
        try {
            Thread.sleep(1000*1);
            t1.interrupt();//中断线程
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

4.线程执行顺序(join)

线程执行插队的一个方法,保证调用join方法的线程在当前正在执行的线程先执行

public class Join {
    public static void main(String[] args) {

        Thread t1 = new Thread() {
            public void run() {
                for (int i = 0; i < 10; i++) System.out.println(Thread.currentThread().getName() + "===>" + i);
            }
        };


        Thread t2 = new Thread() {
            public void run() {
                try {
                    t1.join();
                } catch (InterruptedException e) {
                }
                for (int i = 0; i < 10; i++) System.out.println(Thread.currentThread().getName() + "===>" + i);
            }
        };

        Thread t3 = new Thread() {
            public void run() {
                try {
                    t2.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                for (int i = 0; i < 10; i++) System.out.println(Thread.currentThread().getName() + "===>" + i);
            }
        };

        t1.setName("t1");
        t2.setName("t2");
        t3.setName("t3");
        t1.start();
        t2.start();
        t3.start();
    }
}

5.volatile

1)指令重排
一个线程给赋值,一个线程读出来。(有可能线程赋值在一次时间片中就执行完毕了,看不效果)
大量的循环次数,是一定会出现指令重排的情况的

public class Test {
    public static void main(String[] args) {
        for(int i = 0 ; i<1000*500;i++){
            Zy z = new Zy();
            ThreadSet set = new ThreadSet(z);
            ThreadGet get = new ThreadGet(z);
            set.start();
            get.start();
        }
    }

    static class Zy{
        public int a = 0 ;
        public int b = 0 ;
        public int c = 0 ;
        public int d = 0 ;
    }

    static class ThreadSet extends Thread{
        Zy z = new Zy();
        ThreadSet(Zy z){
            this.z=z;
        }
        public void run(){
            z.a=1;
            z.b=1;
            z.c=1;
            z.d=1;
        }
    }

    static class ThreadGet extends Thread{
        Zy z = new Zy();
        ThreadGet(Zy z){this.z=z;}
        public void run(){
            if(z.b==1&&z.a==0) System.out.println("b==1");
            if(z.c==1&&(z.a==0||z.b==0)) System.out.println("c==1");
            if(z.d==1&&(z.a==0||z.b==0||z.c==0)) System.out.println("d==1");
        }
    }

}

2)保证可见性

public class T_look {
    volatile static boolean flag = true;
    public static void main(String[] args) throws InterruptedException {
        new Thread(){
           public void run(){
               System.out.println("线程正在运行");
               while(flag){}
               System.out.println("线程结束");
           }
        }.start();

        TimeUnit.SECONDS.sleep(2);
        flag = false;
    }
}

6.synchronized

保证原子性和可见性(在释放锁之前会将修改的变量同步到主内存中,保证可见性)
1)代码块,锁住一个对象,对象锁
2)实例方法,锁住当前this对象,对象锁
3)静态方法,锁住当前类,类锁
只有得到锁才会抢时间片进入运行状态

7.Lock

1)是一个提供锁机制的借口,比synchronized功能更加全面,需要自己手动释放锁
2)在资源竞争不激烈(线程少)的情况下,性能差不多,在竞争激烈的情况下,lock性能要好很多

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class T_lock {
    static int ticket = 2;
    static Lock lock = new ReentrantLock();
    public static void main(String[] args ) {
        for(int i = 0 ; i<1000*50;i++){
            ticket = 2;
            Ticket t1 = new Ticket();
            Ticket t2 = new Ticket();
            t1.start();
            t2.start();
        }
    }
    static class Ticket extends Thread{
        public void run(){
            while(true){
                lock.lock();//上锁
                try{
                    if(ticket<=0) break;
                    ticket--;
                    //System.out.println(Thread.currentThread().getName()+"卖出一张,剩余:"+ticket);
                }
                finally{
                    lock.unlock();//释放锁
                }
            }
            if(ticket<0) System.out.println("出错锁");
        }
    }
}

试图抢锁

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Try_lock {
    static Lock lock = new ReentrantLock();
    public static void main(String[] args) {
        new GetLock().start();
        new TryLock().start();
    }
    static class GetLock extends Thread{
        public void run(){
            lock.lock();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally{
                lock.unlock();
            }
        }
    }

    static class TryLock extends Thread{
        public void run(){
            while(true)
            if(!lock.tryLock()) System.out.println("没有抢到锁");
            else {
                System.out.println("抢到锁");
                break;
            }
        }
    }
}

8.线程交替打印

import com.sun.javafx.tk.quantum.QuantumToolkit;
import sun.management.snmp.jvmmib.JvmThreadInstanceTableMeta;

public class Test {
    public static void main(String[] args) {
        Number number = new Number();
        JThread jThread = new JThread(number);
        OThread oThread = new OThread(number);
        jThread.setName("奇数");
        oThread.setName("偶数");
        jThread.start();
        oThread.start();

    }

    static class JThread extends Thread{
        Number number;
        JThread(Number number){
            this.number=number;
        }
        public void run(){
            while(true){
                synchronized (number){
                    if(!(number.num<=100)) break;
                    if(number.num%2==1){
                        System.out.println(Thread.currentThread().getName()+"===>"+number.num++);
                        number.notify();
                    }
                    else {
                        try {
                            number.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    static class OThread extends Thread{
        Number number;
        OThread(Number number){
            this.number=number;
        }
        public void run(){
            while(true){
                synchronized (number){
                    if(!(number.num<=100)) break;
                    if(number.num%2==0){
                        System.out.println(Thread.currentThread().getName()+"===>"+number.num++);
                        number.notify();
                    }
                    else {
                        try {
                            number.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    static class Number{
        int num = 1;
    }
}

9.消费者和生产模式

其实和交替一样,就是控制语句变成了一个容量,缓冲区没有数据了唤醒生产者生产
在这里插入图片描述

三、线程池

1.概念引入

1)不断的创建和销毁线程会消耗大量得资源
2)使用线程池的好处:
①降低资源损耗。重复利用已经创建的线程完成任务
②提高任务的响应速度,执行任务不需要创建的新的线程
③提高线程的可管理性,线程是稀缺资源,不停的创建会消耗系统资源,使用线程池可以对线程进行统一的管理,调优和监控

2.Executor(执行者)

1)是Java中得一个接口,里面只有一个方法execute执行方法,还有一个常用子接口ExecutorService

public interface Executor {

    /**
     * Executes the given command at some time in the future.  The command
     * may execute in a new thread, in a pooled thread, or in the calling
     * thread, at the discretion of the {@code Executor} implementation.
     *
     * @param command the runnable task
     * @throws RejectedExecutionException if this task cannot be
     * accepted for execution
     * @throws NullPointerException if command is null
     */
    void execute(Runnable command);
}

2)常用线程ThreadPoolExecutor继承AbstractExecutorService实现了ExecutorService接口

3.参数以及任务流程

1)参数
①corePoolSize:核心线程数,固定存在的线程
②maximumPoolSize:最大线程数,如果任务队列满了的话,会创建临时线程去执行任务
③keepAliveTime:临时线程存活时间,核心线程默认不超时

executor.allowCoreThreadTimeOut(true);//和临时线程共享超时时间

④unit:存活时间单位
⑤workQueue:任务队列(见后面的详解)
⑥ThreadFactory:线程工厂,使用默认的就可以。
⑦RejectedExecutorHandler:拒绝策略
2)线程池执行任务流程
①判断当前线程数量是否小于核心线程数(当前线程执行),addWorker(command,true)
②进入任务队列,等待空闲线程getTask(),先进先出执行,workQueue.offer(command);
③如果任务队列已满,当前线程数小于最大线程数创建一个临时线程(设置存活时间,过时释放资源)去执行添加的任务(不是队列中得第一个任务),addWorker(command,false)
④如果核心线程,任务队列,最大线程都满了的话,那么会进入饱和策略,默认是抛出异常,丢弃任务

/*If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task */
 int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);

4.任务队列和饱和策略

1)SynchronousQueue(同步队列)
队列中只能存储一个任务,如果队列中已有任务,那么存入任务的线程将被阻塞,同理如果队列中没有任务,那么取出队列中的任务的线程也会被阻塞
在这里插入图片描述

2)LinkedBlockingQueue,基于链表的无界阻塞队列
默认容量大小为Integer的最大值,也可以设置长度
在这里插入图片描述

3)ArrayBlockingQueue,基于数组的有界的阻塞队列
必须设置容量大小
在这里插入图片描述
4)饱和策略
①AbortPolicy:在执行任务时抛出异常,丢弃任务,默认
②CallerRunPolicy:不抛弃任务,调用线程池的线程帮忙执行任务(意思是当前提交任务的线程帮忙执行)
③DiscardPolicy:丢弃任务,不抛出异常
④Discard01destPolicy:丢弃任务( 队首任务,也就时最早加入加入的任务)不抛出异常,

5.ExecutorService常用方法

1)executor.shutdown();等待任务队列中得所有任务执行完毕之后关闭
2)executor.shutdownNow();立即关闭,返回任务队列中得所有任务的list集合

6.Executors工具类(快速创建ThreadPoolExecutor)

1)newCachedPool:核心线程为0,最大线程为int的最大值,存活时间60S,任务队列同步
①不断创建新的线程执行任务,如果有空闲的线程由空闲线程执行,如果超过存活时间会被回收。
②缺点:如果任务量大的话,可能会创建大量的线程。

 return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());

2)newFixedThreadPool:指定固定线程数量的线程执行任务
①只有指定个线程数量执行任务,没有临时线程,存活时间没必要,任务队列是链表(大小为int的最大值)
②任务存在任务队列中,等待空闲线程依次执行

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,	
                                      new LinkedBlockingQueue<Runnable>());
    }

3)newSingleThreadExecutor:最大线程和核心线程为1,单一线程
①和newFiexedThreadPool的简单版本,始终只有一个线程执行任务

 public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

4)newScheduledThreadPool:指定核心线程数
①scheduled方法中设定多少时间后执行任务
②在scheduleAtFixedRate(),设定任务,延长多少时间开始执行,每隔多少时间执行一次,单位

    public static void scheduled(){
        ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(5);
        scheduled.schedule(()->{System.out.println(num++);},2, TimeUnit.SECONDS);
        scheduled.scheduleAtFixedRate(()->{System.out.println(num++);},0,2,TimeUnit.SECONDS);
    }

7.源码解读

1)ThreadFactory :管理线程的生命周期,默认 Executors.defaultThreadFactory()

    private volatile ThreadFactory threadFactory;
// Executors中
  static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;
       }

2)工作线程:管理任务和工作线程

    private final HashSet<Worker> workers = new HashSet<Worker>();
    private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable{
        final Thread thread;//执行任务的线程
        /** Initial task to run.  Possibly null. */
        Runnable firstTask;//任务

        /**
         * Creates with given first task and thread from ThreadFactory.
         * @param firstTask the first task (null if none)
         */
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;//创建任务
            this.thread = getThreadFactory().newThread(this);//从线程工厂中获取线程
        }
        
        public void run() {runWorker(this);}

        final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) { // 线程处理完当前任务之后,获取任务队列中的任务,getTask方法中,设置了获取队列中的任务,如果超过了存活时间,那么返回为NULL
            
                  w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        task.run(); //执行任务
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }

   private Runnable getTask() {
     try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : //临时线程寻找任务,超时销魂
                    workQueue.take(); //如果是核心线程,没有任务会阻塞
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
   }
}

3)execute方法解读

    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) { //如果当前线程小于核心线程,那么创建一个核心线程处理工作线程
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) { //将任务添加到任务队列中
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))//任务队列已满,创建临时线程处理
            reject(command); //线程已满,采取饱和策略
    }

private boolean addWorker(Runnable firstTask, boolean core) {//根据core创建临时or核心的工作线程
        retry:
        for (;;) {//检查状态
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                if (compareAndIncrementWorkerCount(c))
                    break retry;S
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            w = new Worker(firstTask); //创建工作线程
            final Thread t = w.thread; //线程
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int rs = runStateOf(ctl.get());

                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        workers.add(w);//将work添加到HashSet中
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true; //添加成功
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    t.start(); //启动工作任务
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值