java 线程(中、高级)总结

什么是进程:

        你把它理解成一个软件

什么是线程:

        你把它理解成软件里面的一个功能,做的事情

什么是多线程:

        你把它理解成 软件里面的某一个功能,原先是一个人累死累活的在那里完成,现在好了,多个人一起完成,轻松又快活

什么是线程不安全:

        你把它理解成  软件里面的某一个功能,原先是一个人累死累活的在那里完成,虽然累,但是,数据不会出错,但是现在多个人一起来弄,结果,张三读取的数据是被李四修改过的数据,不是最新的数据,这样就是线程不安全的

什么是线程安全:

         你把它理解成  软件里面的某一个功能,原先是一个人累死累活的在那里完成,虽然累,但是,数据不会出错,但是现在多个人一起来弄,我让你们排好队,一个一个,有序的来操作数据,如果有人正在操作,我就让你在外面等着,必须等前面的人做完了自己的事情,你才能进去

什么是并行:

        你把它理解成  软件里面的某一个功能,同时进行多个任务

什么是并发:

        你把它理解成 软件里面的某一个功能,一下子来了很多请求,如果不处理,会导致程序宕机,卡死等

线程不安全和线程安全,

针对ArrayList   HashSet  HashMap 来进行举例

ArrayLists 线程不安全  ;  Vector、Collections 、CopyOnWriteArrayList 线程安全

HashSet  线程不安全   ; CopyOnWriteArraySet 线程安全

HashMap 线程不安全   ; ConcurrentHashMap 线程安全

下面用代码的方式,来进行模拟一下多线程,

需求:四位售票员进行卖票操作,加锁

package com.japhet.util;

//卖票
class Ticket{

    //总票数
    private int ticket = 3000;

    //卖票逻辑递减
    public synchronized void seal(){
        if(ticket<=0){
            return;
        }
        ticket--;
        System.out.println(Thread.currentThread().getName()+"票还剩余"+ticket);
    }

}

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

        Ticket ticket = new Ticket();
        //售票员1进行卖票
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 4000; i++) {
                    ticket.seal();
                }
            }
        },"AA").start();
        //售票员2进行卖票
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 4000; i++) {
                    ticket.seal();
                }
            }
        },"BB").start();

        //售票员3进行卖票
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 4000; i++) {
                    ticket.seal();
                }
            }
        },"CC").start();

        //售票员4进行卖票
        new Thread(()->{
            for (int i = 0; i < 4000; i++) {
                ticket.seal();
            }
        },"DD").start();

    }
}

下面用代码的方式,来进行模拟一下线程之间的通讯,

需求:因为是多线程,当11线程抢到资源,执行完自己的程序之后,他是不知道下一次会是谁成功的抢到资源,那我可以进行设置谁能抢到资源 (通过 await 等待   signal 通知某个线程执行),这里就让11线程执行了就让22线程去执行,22线程执行了就让33线程去执行,33线程执行了,就让11线程执行,依次循环10次这个动作

package com.japhet.util;

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

class Srouce{
//    线程标识
    private int temp = 1;

//    锁
    private Lock lock =  new ReentrantLock();

//    三个线程
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();

    public void test(){
        lock.lock();
        try {
            while (temp!=1){//防止虚假唤醒,所以得用while循环
                condition1.await();
            }

            System.out.println("线程1111进来了");

            temp = 2;//修改标识,
            condition2.signal();//通知线程2去干活
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void test2(){
        lock.lock();
        try {
            while (temp!=2){//防止虚假唤醒,所以得用while循环
                condition2.await();
            }
            System.out.println("线程2222进来了");
            temp = 3;//修改标识,
            condition3.signal();//通知线程2去干活
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void test3(){
        lock.lock();
        try {
            while (temp!=3){//防止虚假唤醒,所以得用while循环
                condition3.await();
            }
            System.out.println("线程3333进来了");
            temp = 1;//修改标识,
            condition1.signal();//通知线程2去干活
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

public class ThreadUtils2 {
    public static void main(String[] args) {
        Srouce srouce = new Srouce();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                srouce.test();
            }
        },"11").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                srouce.test2();
            }
        },"22").start();


        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                srouce.test3();
            }
        },"33").start();
    }

}

什么是死锁

多个线程同事进行操作

线程A持有A锁

线程B持有B锁

程序还在进行中,都没有释放锁,然后线程A要获取线程B的内容 ,同时,线程B也要获取线程A的内容,那这个时候,线程A、B都处于互相等待的过程中,就会陷入死循环,你等我释放,我也再等你释放

什么是公平锁和非公平锁

非公平锁:(速度快,效率高)

        比如我有三个线程 A,B,C ,当我运行的时候,可能A线程的能力强,那么一套程序运行下来,A线程的运行概率多很多,其他B,C线程基本上没怎么动,会被饿死,这个就是非公平锁的概念

公平锁:(速度慢,效率低)

        比如我有三个线程 A,B,C ,当我运行的时候,会相对公平的分摊到每个线程里面去,看源码 

 

FutureTask 未来任务使用demo

package com.japhet.util;

import java.util.concurrent.FutureTask;

public class FutureTaskDemo {
    public static void main(String[] args) {
        System.out.println("主线程开始....."+Thread.currentThread().getName());
        FutureTask<Integer> futureTask = new FutureTask<>(()->{
            System.out.println("--------"+Thread.currentThread().getName());
            return 1;
        });
        new Thread(futureTask).start();
        try {

            while (!futureTask.isDone()){
                System.out.println("线程还没有执行完,等待...."+Thread.currentThread().getName());
            }


            Integer num = futureTask.get();
            Integer num2 = futureTask.get();

            System.out.println("最后结果 "+num);
            System.out.println("最后结果2 "+num2);

            System.out.println("主线程结束....."+Thread.currentThread().getName());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

效果图

什么是阻塞队列 (BlockingQueue)

所谓队列:即 先进先出 ,队列有大小限制

线程A可以往队列里面放元素,线程B可以获取队列里面的元素,如果这个队列元素被线程A放满了,就阻塞状态 ; 如果线程B把队列里面的数据获取完了,也成阻塞状态 

什么是线程池 (Executors)

避免资源浪费开销,用到线程就开一个线程,不用线程就关闭线程,这种频繁的开关,就会造成资源浪费

创建线程池

package com.japhet.util;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolDemo {

    public static void main(String[] args) {
        // 定义线程池里面初始化10个线程
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        // 定义线程池里面就1个线程
        ExecutorService executorService2 = Executors.newSingleThreadExecutor();
        //定义线程池里面就n个线程,可扩容,根据你调用
        ExecutorService executorService3 = Executors.newCachedThreadPool();
        try {
            for (int i = 0; i < 20; i++) {
                final int x = i;
//                executorService.execute(()->{
//                executorService2.execute(()->{
                executorService3.execute(()->{
                    System.out.println(x+"线程---"+Thread.currentThread().getName());
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
//            关闭
//            executorService.shutdown();
//            executorService2.shutdown();
            executorService3.shutdown();
        }
    }
}

(注意: 不推荐使用这三种方式进行创建线程池,因为阿里巴巴java开发手册提到,如果用这三种方式进行创建,会造成OOM内存溢出,会堆积大量的请求,来不及处理,一般都是自己进行创建线程池,传入7个参数

java创建线程池有哪几种,为什么用线程池,以及拒绝策略

为什么要用线程池:不需要反复的创建和销毁线程,因为比较耗资源,提高管理性,统一分配调优监控

newSingleThreadPool、newFixedThreadPool、newCachedThreadPool、newScheduledThreadPool、newWorkStealingPool

里面的参数:

corePoolSize : 表示线程池核心线程数,当初始化线程池时,会创建核心线程进入等待状态,即使它是空闲的,核心线程也不会被摧毁,从而降低了任务一来时要创建新线程的时间和性能开销。

maximumPoolSize : 表示最大线程数,意味着核心线程数都被用完了,那只能重新创建新的线程来执行任务,但是前提是不能超过最大线程数量,否则该任务只能进入阻塞队列进行排队等候,直到有线程空闲了,才能继续执行任务。

keepAliveTime : 表示线程存活时间,除了核心线程外,那些被新创建出来的线程可以存活多久。意味着,这些新的线程一但完成任务,而后面都是空闲状态时,就会在一定时间后被摧毁。

unit : 存活时间单位。

workQueue : 表示任务的阻塞队列,由于任务可能会有很多,而线程就那么几个,所以那么还未被执行的任务就进入队列中排队,队列我们知道是 FIFO 的,等到线程空闲了,就以这种方式取出任务。这个一般不需要我们去实现。

threadFactoy: 线程工厂

拒绝策略(handler):当workqueue满了,maximumPoolSize也满了,就应该要拒绝新的线程进来 ,常见四种拒绝策略

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。

ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。

ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务

ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务

自定义线程池

package com.japhet.util;

import java.util.concurrent.*;


//创建自定义线程池,
// 只能处理 循环里面的前8个,后面的12个就被拒绝策略给拒绝了
public class ThreadPoolDemo2 {

    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,
                5,
                2L,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );

        try {
            for (int i = 0; i < 20; i++) {
                final int x = i;
                threadPoolExecutor.execute(()->{
                    System.out.println(x+"线程---"+Thread.currentThread().getName());
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            threadPoolExecutor.shutdown();
        }

    }
}

猜你喜欢

转载自blog.csdn.net/Japhet_jiu/article/details/129166010