java总结笔记3,多线程

导览

基础概念

并行并发

两个事情同时发生就是并行,两个事件在同一个极小的时间段中发生就是并发。

进程线程

进程是一个应用的实例,线程是在进程中的一个独立的执行单元,进程中包含了很多线程。

多线程

多个线程并发执行。

多种线程创建方式

继承thread类

import java.util.Date;

public class threadextends Thread{
        @Override
    public void run(){
        for (int i =0;i<5;i++){
            System.out.println("现在是子线程执行,时间为:"+System.currentTimeMillis());

        }
    }
}
class main{
    public static void main(String[] args) {
        thread类 thread类 = new thread();
        thread类.start();
        for (int i=0;i<5;i++){
            System.out.println("现在是主线程执行,时间:"+System.currentTimeMillis());
        }
    }
}

实现runnable接口

public class runnableimplements Runnable{
    @Override
    public void run() {
        for (int i=0;i<5;i++){
            System.out.println("当前执行的子线程,时间为:"+System.currentTimeMillis());
        }
    }
}
class mainrun{
    public static void main(String[] args) {
        Thread thread=new Thread(new runnable(),"aas");
        thread.start();
        for (int  i = 0;i<5;i++){
            System.out.println("当前线程"+thread.getName()+"当前时间为:"+System.currentTimeMillis());
        }

    }
}

实现callable接口

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class callableimplements Callable<String> {

    @Override
    public String call() throws Exception {
        for (int i=0;i<5;i++){
            System.out.println(Thread.currentThread().getName()+"正在执行中,时间为:"+System.currentTimeMillis());
        }
        return "callable类执行完成";
    }
}
class main1{
    public static void main(String[] args) {
        FutureTask<String> task = new FutureTask<String>(new callable());
        Thread thread = new Thread(task);
        thread.start();
        for(int i=0;i<5;i++){
            System.out.println(Thread.currentThread().getName()+"执行中,时间为:"+System.currentTimeMillis());
        }
        try {
            System.out.println(task.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

线程池

java内部提供了一个excutors工具类来包装对线程池的操作
在这里插入图片描述

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

public class runnableimplements Runnable{
    static int k=0;

    @Override
    public void run() {
        for (int i=0;i<5;i++){
            System.out.println("当前执行的"+Thread.currentThread().getName()+",时间为:"+System.currentTimeMillis()+"执行次数为:"+i);
            k++;
        }
    }
}
class mainrun{
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        executorService.execute(new runnable());
        for (int i = 0;i<5;i++){
            System.out.println("当前执行的"+Thread.currentThread().getName()+",时间为:"+System.currentTimeMillis()+"执行次数为:"+i);
        }


    }
}

四种方式比较

1.实现接口和继承thread

  • 接口更加适合多个相同程序共享同一个资源
  • 接口可以避免java中单继承的局限性
  • 接口代码可以被多个线程共享,代码和线程独立
  • 线程池只支持runacle和callable的线程,不能直接放入继承thread的类
  • 扩充,在Java中每次程序运行至少启动两个线程一个是主线程一个是垃圾收集线程
    2.runnable和callable比较
  • 两个都是接口
  • 两个都可以写多线程程序
  • 两个都要通过调用thread.start()来启动线程
  • (不同点)实现callable接口能返回执行结果,runnable不能返回结果
  • (不同点)callable接口的call允许抛出异常,而runnable的run不允许抛出以异常
  • (不同点)实现callable接口的线程可以调用future.cancel取消执行,实现runnable接口的线程则不能
  • (总结)当在线程中需要实时监控线程的运行状态时候推荐使用callable
  • 在调用FutureTask.get()获取返回结果的时候,主线程会阻塞直到获取到结果,而不调用这个方法的时候线程不会阻塞

多线程特性

原子性

即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行

可见性

可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值

有序性

有序性即程序执行的顺序按照代码的先后顺序执行

线程生命周期

新建

  • new关键字船舰一个线程之后,该线程就处于新建阶段
  • jvm为线程分配内存,初始化成员变量

就绪

  • 当线程对象调用start的时候,线程就处于就绪状态
  • jvm为线程创建方法栈和程序计数器,等待线程调度器调度

运行

  • 就绪状态的线程获取cpu资源,开始运行run,该线程进入运行状态

阻塞

  • 线程调用sleep方法主动放弃所占用的处理器资源
  • 线程调用阻塞io方法,在该方法返回之前该线程被阻塞
  • 线程试图获得一个同步锁(同步监视器),但是该同步锁正在被其他线程所持有
  • 线程在等待某个通知(notify)
  • 程序调用了线程的suspend方法将线程挂起,在获取同步锁的时候将线程挂起容易造成死锁

死亡

run或者call方法执行完成的时候,线程正常结束
线程抛出一个未捕获的异常
调用该线程的stop方法来结束该线程,该方法容易导致思索

线程通讯

休眠唤醒

Object方式的wait、notify、notifyAll

public class object线程通讯 {
    private Object obj = new Object();
    static int flag= 0;
    public void even(){
        while (flag<10){
            synchronized (obj){
                if (flag%2==0){
                    System.out.println("偶数"+flag++);
                    obj.notify();
                }else {
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    public void odd(){
        while (flag<10){
            synchronized (obj){
                if (flag%2!=0){
                    System.out.println("奇数"+flag++);
                    obj.notify();
                }else {
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}
class mainrun{
    public static void main(String[] args) {
        object线程通讯 o = new object线程通讯();
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                o.even();
            }
        });
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                o.odd();
            }
        });
        thread1.start();
        thread2.start();
    }
}

Condition的await、signal、signalAll

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

public class Condition线程通讯 {

    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    private int flag = 0;

    public void even(){
        while (flag<10){
            lock.lock();
            try{
                if (flag%2==0){
                    System.out.println("偶数"+flag++);
                    condition.signal();
                }else {
                    try {
                        condition.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }finally {
                lock.unlock();
            }
        }
    }
    public void odd(){
        while (flag<10){
            lock.lock();
            try{
                if (flag%2!=0){
                    System.out.println("奇数"+flag++);
                    condition.signal();
                }else {
                    try {
                        condition.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }finally {
                lock.unlock();
            }
        }
    }
}
class mainr{
    public static void main(String[] args) {
        Condition线程通讯 o = new Condition线程通讯();
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                o.even();
            }
        });
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                o.odd();
            }
        });
        thread1.start();
        thread2.start();
    }
}

Object和Condition区别比较

  • object wait()必须在synchronized(同步锁)下使用,
  • object wait()必须要通过Nodify()方法进行唤醒
  • condition await() 必须和Lock(互斥锁/共享锁)配合使用
  • condition await() 必须通过 signal() 方法进行唤醒

CountDownLatch

形容:使一个线程等待其他线程完成各自的工作后再执行

import java.util.concurrent.CountDownLatch;

public class CountDownLatch方式 {
    //创建一个CyclicBarrier对象表示在三个线程之后要求的线程进行调用
    CountDownLatch countDownLatch = new CountDownLatch(3);

    public void racer(){
        String name = Thread.currentThread().getName();
        System.out.println(name+"运动员正在准备中。。。");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(name+"运动员准备完毕!");
        countDownLatch.countDown();
    }
    public void coach(){
        String name = Thread.currentThread().getName();
        System.out.println("等待运动员中");
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("所有运动员已经准备好了,开始训练!");
    }

    public static void main(String[] args) {
        CountDownLatch方式 cyclicBarrier方式  = new CountDownLatch方式();

        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                cyclicBarrier方式.racer();
            }
        },"一号");
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                cyclicBarrier方式.racer();
            }
        },"二号");
        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                cyclicBarrier方式.racer();
            }
        },"三号");
        Thread thread4 = new Thread(new Runnable() {
            @Override
            public void run() {
                cyclicBarrier方式.coach();
            }
        },"教练");

        thread4.start();
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

CyclicBarrier方式

形容:一组线程等待至某个状态之后再全部同时执行)

import java.util.Date;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrier方式 {
    private CyclicBarrier cyclicBarrier= new CyclicBarrier(4);
    public void startthread(){
        String name = Thread.currentThread().getName();
        System.out.println(name+"线程正在准备中。。。。。");
        try {
            cyclicBarrier.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
        System.out.println(name+"线程已经启动完毕,当前时间为"+new Date().getTime());
    }

    public static void main(String[] args) {
        CyclicBarrier方式 cyclicBarrier方式 = new CyclicBarrier方式();
        new Thread(new Runnable() {
            @Override
            public void run() {
                cyclicBarrier方式.startthread();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                cyclicBarrier方式.startthread();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                cyclicBarrier方式.startthread();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                cyclicBarrier方式.startthread();
            }
        }).start();
    }

}

Semaphore方式

形容(用于控制对某组资源的访问权限,八个人三台机器,机器是互斥资源)

import java.util.concurrent.Semaphore;

public class Semaphore方式 {

    static  class work implements Runnable{
        int worknum;
        Semaphore semaphore;

        public work(int worknum, Semaphore semaphore) {
            this.worknum = worknum;
            this.semaphore = semaphore;
        }


        public void run(){
            try {
                //获取机器
                semaphore.acquire();
                //开始工作
                String name = Thread.currentThread().getName();
                System.out.println(name+"获取到机器开始使用。。。。。,当前时间:"+System.currentTimeMillis());
                Thread.sleep(1000);
                //解除对机器的占用
                semaphore.release();
                System.out.println(name+"使用完毕,释放机器使用权!!!,当前时间:"+System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public static void main(String[] args) {
            int works = 8;
            Semaphore semaphore= new Semaphore(3);
            for (int i=0;i<works;i++){
                new Thread(new work(works,semaphore)).start();
            }
        }
    }
}

内存模型

执行流程

在这里插入图片描述

内存模型

在这里插入图片描述

java内存

程序计数器

  • 每一个线程都对有一个线程计数器
  • 各线程的程序计数器是线程私有的,互不影响,是线程安全的
  • 程序计数器记录线程正在执行的内存地址,以便被中断线程恢复执行时再次按照中断时的指令地址继续执行

Java栈JavaStack(虚拟机栈JVM Stack)

在这里插入图片描述

  • 每一个线程一个java栈
  • 每个java栈是由若干个栈帧组成
  • 每一个线程栈帧都对应一个方法
  • 栈帧在方法运行时,创建并入栈;方法执行完,该栈帧弹出栈帧中的元素作为该方法返回值,该栈帧被清除
  • 栈顶的栈帧叫活动栈,表示当前执行的方法,才可以被CPU执行
  • 线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常
  • 栈是可以动态拓展的,当栈扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常
  • 引用对象的保存都是在堆里,栈帧里面只是保留了一个引用地址

方法区MethodArea

  • 方法区是Java堆的永久区(PermanetGeneration)
  • 方法区存放了要加载的类的信息(名称、修饰符等)、类中的静态常量、类中定义为final类型的常量、类中的Field信息、类中的方法信息,
  • 方法区是被Java线程共享的
  • 方法区要使用的内存超过其允许的大小时,会抛出OutOfMemoryError: PremGen space的错误信息。

常量池ConstantPool

  • 常量池是方法区的一部分。
  • 常量池中存储两类数据:字面量和引用量(类,接口)。
  • 字面量:字符串、final变量等。
  • 引用量:类/接口、方法和字段的名称和描述符,
  • 常量池在编译期间就被确定,并保存在已编译的.class文件中

本地方法栈Native Method Stack

  • java栈是执行用户的java方法,本地方法栈则是为jvm服务的

线程安全

问题

  • 多个线程在操作共享数据的时候出现线程安全问题
  • 操作共享数据线程代码有多余
  • 多个数据对共享数据有写操作

线程同步

同步代码块(synchronized)

synchronized表示对这个区域的资源实行互斥访问,在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着。
给代码加一个锁,只有获取到下面的obj对象的时候才能调用这个方法

public class Ticket implements Runnable{
    private int num = 20;
    String obj = "dasdasd";
    @Override
    public void run() {
        synchronized (obj){
            while (true){
                if (num<0){
                    break;
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                String name = Thread.currentThread().getName();
                System.out.println("线程:"+name+"售票:"+num--+"当前时间:"+System.currentTimeMillis());
            }
        }

    }
}
class maint{
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        Thread thread1 = new Thread(ticket,"一号窗口");
        Thread thread2 = new Thread(ticket,"二号窗口");
        Thread thread3 = new Thread(ticket,"三号窗口");
        thread1.start();
        thread2.start();
        thread3.start();

    }
}

同步方法(synchronized)

用synchronized修饰的方法就是同步方法,在线程a执行方法的时候其他线程只能在方法外面等待
同步锁对象问题:

  • 对于非static方法,同步锁就是this。
  • 对于static方法,同步锁是当前方法所在类的字节码对象(类名.class)。
public class Ticket implements Runnable{
    private int num = 20;
    @Override
    public void run() {
        tick();
    }
    public synchronized void tick(){
        while (true){
            if (num<0){
                break;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            String name = Thread.currentThread().getName();
            System.out.println("线程:"+name+"售票:"+num--+"当前时间:"+System.currentTimeMillis());
        }
    }
}
class maint{
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        Thread thread1 = new Thread(ticket,"一号窗口");
        Thread thread2 = new Thread(ticket,"二号窗口");
        Thread thread3 = new Thread(ticket,"三号窗口");
        thread1.start();
        thread2.start();
        thread3.start();

    }
}

同步锁(ReenreantLock)

  • lock和unlock一定要同时出现,不然会造成死锁状态
  • ReenreantLock提供了比synchronized跟多的方法
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Ticket implements Runnable{
    private int num = 20;
    Lock lock = new ReentrantLock(false);

    @Override
    public void run() {
        while (true){
            lock.lock();
            try {
                if (num<0){
                break;
            }
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
            String name = Thread.currentThread().getName();
            System.out.println("线程:"+name+"售票:"+num--+"当前时间:"+System.currentTimeMillis());
        }
    }
}
class maint{
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        Thread thread1 = new Thread(ticket,"一号窗口");
        Thread thread2 = new Thread(ticket,"二号窗口");
        Thread thread3 = new Thread(ticket,"三号窗口");
        thread1.start();
        thread2.start();
        thread3.start();

    }
}

Synchronized和Lock区别

  • synchronized是java内置关键字,在jvm层面,Lock是个java类;
  • synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;
  • synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),- - Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
  • 用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;
  • synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)
  • Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。
  • (总结)lock比较灵活,synchronized非公平,除非当前线程使用完,不然当前资源一直都是当前线程独占

特殊域变量(volatile)

局部变量(ThreadLocal)

阻塞队列(LinkedBlockingQueue)

原子变量(Atomic*)

死锁问题

多个线程竞争资源造成一种僵局,互相申请互相占用

发生条件

互斥条件

进程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。

不可剥夺条件

进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放(只能是主动释放)。

请求与保持条件

进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。

循环等待

不一定是死锁但是死锁一定有循环等待

死锁处理

死锁预防(破坏四个必要条件)
  • 破坏互斥条件(无法破坏,所以一般选择去处理其他几个条件)
  • 破坏占有并等待条件(在拥有资源的时候不允许申请其他资源)
  • 方法一:一次性分配资源,在创建线程的时候一次性要求她所需要的全部资源
  • 方法二:要求每个线程在提出新的资源申请的时候释放掉他所占有的资源
  • 破坏不可抢占条件(准许资源根据优先级进行抢夺)
  • 一个进程如果申请另一个线程所占用的资源的时候,操作系统可以要求另外一个线程释放资源
  • 破坏循环等待条件
死锁避免
  • 有序资源分配,将系统中的所有资源标号,按顺序申请资源
  • 银行家算法,在分配资源的时候先判断系统是否安全
  • 顺序加锁
  • 限时加锁(给线程一个超时时间,在时间外获取不到资源的话就放弃当前资源)
  • 死锁检测和恢复

多线程控制

ThreadLocal

作用

提供局部变量,即使用相同变量的每一个线程维护一个该变量的副本,当某些数据是一线程作为作用域并且不同线程具有不同的数据副本的时候就可以采用threadlocal,比如数据库链接connection,每一个请求都需要但是又不互相影响

总结

每个线程在调用threadlocal的时候都会创建一个副本,线程之间的数据不共享,线程内部的数据修改不会影响到别的线程
示例:

public class threadlocal示例 {
    static class bank{
        private ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
            protected Integer initialValue(){
                return 0;
            }
        };

        public  Integer get(){
            return  threadLocal.get();
        }
        public void set(Integer money){
            threadLocal.set(threadLocal.get()+money);
        }

    }
    static class transfer implements Runnable{
        private bank bank;

        public transfer(bank bank) {
            this.bank=bank;
        }

        @Override
        public void run() {
            for (int i = 0; i < 10 ; i++) {
                bank.set(10);
                System.out.println(Thread.currentThread().getName()+"账户余额"+bank.get());
            }
        }
    }

    public static void main(String[] args) {
        bank bank = new bank();
        transfer transfer = new transfer(bank);
        new Thread(transfer,"客户一").start();
        new Thread(transfer,"客户二").start();
    }
}

原子类

使用原子类来保证i++操作的原子性

new AtomicInteger(0).getAndIncrement();//n++
new AtomicInteger(0).incrementAndGet();//++n
new AtomicInteger(0).decrementAndGet();//--n
new AtomicInteger(0).getAndDecrement();//n--

原理:先根据getintvolatile来获取预期得到的值,然后通过compareandswapint来判断当前值和预期值是否相等,相等说明没有线程修改过当前值,不相等就重新获取新的值再继续判断
在这里插入图片描述

总结:

  • AtomicInteger会降低性能
  • AtomicInteger存在aba的问题,也就是线程获取当前值a,然后修改为b然后再修改为a的情况

lock类

在这里插入图片描述

ReentrantLock(可重入锁)线程可以进入他已经拥有的锁的同步代码块

  • 不可重入锁:线程请求他已经拥有的锁时候会阻塞
  • ReentrantLock lock = new ReentrantLock();
  • lock.lock();lock.unlock();

ReentrantReadWriteLock(读写锁)

即可以同时读,读的时候不能写;不能同时写,写的时候不能读。
示例代码:

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockDemo {
    private Map<String ,String> map = new HashMap<String, String>();
    private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    ReentrantReadWriteLock.ReadLock readLock= readWriteLock.readLock();
    ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();

    public String get(String kep){
        readLock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+"读取操作开始执行,当前时间为:"+System.currentTimeMillis());
            Thread.sleep(3000);
            return map.get(kep);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }finally {
            System.out.println("读操作已解锁");
            readLock.unlock();
        }
    }
    public String put(String key,String value){
        writeLock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+"写入操作开始执行,当前时间为:"+System.currentTimeMillis());
            Thread.sleep(3000);
            return map.put(key,value);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }finally {
            System.out.println("写操作已解锁,当前时间为:"+System.currentTimeMillis());
            writeLock.unlock();
        }
    }

    public static void main(String[] args) {
        ReadWriteLockDemo readWriteLockDemo = new ReadWriteLockDemo();
        readWriteLockDemo.put("aaa","asda");

        new Thread("进程1"){
            @Override
            public void run() {
                readWriteLockDemo.get("aaa");
            }
        }.start();
        new Thread("进程2"){
            @Override
            public void run() {
                readWriteLockDemo.get("aaa");
            }
        }.start();
        new Thread("进程3"){
            @Override
            public void run() {
                readWriteLockDemo.get("aaa");
            }
        }.start();
        new Thread("进程4"){
            @Override
            public void run() {
                readWriteLockDemo.get("aaa");
            }
        }.start();
    }
}

Volatile关键字

  • 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。(注意:不保证原子性)
  • 禁止进行指令重排序。(保证变量所在行的有序性)
int i;
i = 0;
i = 1;
i = 2;//前两个将会被编译器忽略,但是如果加了关键字Volatile就不存在忽略情况

应用场景:

对变量的写操作不依赖于当前值(在对变量赋值的时候,前面是啥值和他无关)
该变量没有包含在具有其他变量的不变式中

常用场景:

状态量标记:
volatile boolean flag = false;
 
while(!flag){
    doSomething();
}
 
public void setFlag() {
    flag = true;
}
双重校验
//单例模式

线程池

多线程缺点:

  • 处理任务的线程创建和销毁都非常耗时并消耗资源。
  • 多线程之间的切换也会非常耗时并消耗资源。

ThreadPoolExecutor

在Java中创建线程池常用的类是ThreadPoolExecutor,该类的全参构造函数如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {

参数介绍:

  • corePoolSize:线程池中核心线程数的最大值
  • maximumPoolSize:线程池中能拥有最多线程数
  • workQueue:用于缓存任务的阻塞队列,对于不同的应用场景我们可能会采取不同的排队策略,这就需要不同类型的阻塞队列,在线程池中常用的阻塞队列有以下2种:
  • SynchronousQueue:此队列中不缓存任何一个任务。向线程池提交任务时,如果没有空闲线程来运行任务,则入列操作会阻塞。当有线程来获取任务时,出列操作会唤醒执行入列操作的线程。从这个特性来看,SynchronousQueue是一个无界队列,因此当使用SynchronousQueue作为线程池的阻塞队列时,参数maximumPoolSizes没有任何作用。
  • LinkedBlockingQueue:顾名思义是用链表实现的队列,可以是有界的,也可以是无界的,但在Executors中默认使用无界的。
  • 以上三个参数之间的关系如下:
  1. 如果没有空闲的线程执行该任务且当前运行的线程数少于corePoolSize,则添加新的线程执行该任务。
  2. 如果没有空闲的线程执行该任务且当前的线程数等于corePoolSize同时阻塞队列未满,则将任务入队列,而不添加新的线程。
  3. 如果没有空闲的线程执行该任务且阻塞队列已满同时池中的线程数小于maximumPoolSize,则创建新的线程执行任务。
  4. 如果没有空闲的线程执行该任务且阻塞队列已满同时池中的线程数等于maximumPoolSize,则根据构造函数中的handler指定的策略来拒绝新的任务。
  • keepAliveTime:表示空闲线程的存活时间。
  • unit:表示keepAliveTime的单位。
  • handler:表示当workQueue已满,且池中的线程数达到maximumPoolSize时,线程池拒绝添加新任务时采取的策略。一般可以采取以下四种取值。
    1.ThreadPoolExecutor.AbortPolicy() 抛出RejectedExecutionException异常
    2.ThreadPoolExecutor.CallerRunsPolicy() 由向线程池提交任务的线程来执行该任务
    3.ThreadPoolExecutor.DiscardOldestPolicy() 抛弃最旧的任务(最先提交而没有得到执行的任务)
    4.ThreadPoolExecutor.DiscardPolicy() 抛弃当前的任务
  • threadFactory:指定创建线程的工厂

四种常见线程池

newCachedThreadPool(可缓存线程池)

形容

  • 该方法创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

特点:

  • 工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE)
    空闲的工作线程会自动销毁,有新任务会重新创建
  • 在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪。

示例代码:

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

public class newCachedThreadPool {
    private static int j = 0;
    public static void main(String[] args) {
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

        cachedThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                odd();
            }
        });

    }
    public static void odd(){
        for (int i = 0; i < 10; i++) {
            ++j;
            System.out.println(Thread.currentThread().getName()+"正在执行任务,当前j为:"+j);
        }
    }
}

newFixedThreadPool(固定数量线程池)

形容
  • 该方法创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。
优点
  • 具有线程池提高程序效率和节省创建线程时所耗的开销。
缺点
  • 在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。

newSingleThreadExecutor(单一线程池,不如不使用)

形容:
该方法创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO,优先级)执行。如果这个线程异常结束,会有另一个取代它,保证顺序执行。
单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。

newScheduleThreadPool

形容:该方法创建一个定长的线程池,而且支持定时的以及周期性的任务执行,支持定时及周期性任务执行。

容器

同步容器

Vector

  • 实现了List接口,Vector实际上就是一个数组,和ArrayList类似,但是Vector中的方法都是synchronized方法,即进行了同步措施。

Stack

  • 也是一个同步容器,它的方法也用synchronized进行了同步,它实际上是继承于Vector类。

HashTable

  • 实现了Map接口,它和HashMap很相似,但是HashTable进行了同步处理,而HashMap没有。

Collections

  • Collections类是一个工具提供类,注意,它和Collection不同,Collection是一个顶层的接口。在Collections类中提供了大量的方法,比如对集合或者容器进行排序、查找等操作。最重要的是,在它里面提供了几个静态工厂方法来创建同步容器类。

总结:

  • Hashtable的实现中添加synchronized关键字,保证了线程安全的情况下严重降低了并发性能,在多个线程竞争容器的时候吞吐量严重下降

并发容器

作用:是为了替代原本的同步容器,性能较强

ConcurrentHashMap

对应的非并发容器:HashMap
目标:代替Hashtable、synchronizedMap,支持复合操作
原理:JDK6中采用一种更加细粒度的加锁机制Segment“分段锁”,JDK8中采用CAS无锁算法。

CopyOnWriteArrayList

对应的非并发容器:ArrayList
目标:代替Vector、synchronizedList
原理:利用高并发往往是读多写少的特性,对读操作不加锁,对写操作,先复制一份新的集合,在新的集合上面修改,然后将新集合赋值给旧的引用,并通过volatile 保证其可见性,当然写操作的锁是必不可少的了。

CopyOnWriteArraySet

对应的费并发容器:HashSet
目标:代替synchronizedSet
原理:基于CopyOnWriteArrayList实现,其唯一的不同是在add时调用的是CopyOnWriteArrayList的addIfAbsent方法,其遍历当前Object数组,如Object数组中已有了当前元素,则直接返回,如果没有则放入Object数组的尾部,并返回。

ConcurrentSkipListMap

对应的非并发容器:TreeMap
目标:代替synchronizedSortedMap(TreeMap)
原理:Skip list(跳表)是一种可以代替平衡树的数据结构,默认是按照Key值升序的。Skip list让已排序的数据分布在多层链表中,以0-1随机数决定一个数据的向上攀升与否,通过”空间来换取时间”的一个算法。ConcurrentSkipListMap提供了一种线程安全的并发访问的排序映射表。内部是SkipList(跳表)结构实现,在理论上能够在O(log(n))时间内完成查找、插入、删除操作。

ConcurrentSkipListSet

对应的非并发容器:TreeSet
目标:代替synchronizedSortedSet
原理:内部基于ConcurrentSkipListMap实现
ConcurrentLinkedQueue
不会阻塞的队列
对应的非并发容器:Queue
原理:基于链表实现的FIFO队列(LinkedList的并发版本)

LinkedBlockingQueue、ArrayBlockingQueue、PriorityBlockingQueue

对应的非并发容器:BlockingQueue
特点:拓展了Queue,增加了可阻塞的插入和获取等操作
原理:通过ReentrantLock实现线程安全,通过Condition实现阻塞和唤醒
实现类:
LinkedBlockingQueue:基于链表实现的可阻塞的FIFO队列
ArrayBlockingQueue:基于数组实现的可阻塞的FIFO队列
PriorityBlockingQueue:按优先级排序的队列

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值