Java并发总结

Java并发

进程

进程是程序的一次执行过程,是系统运行程序的基本单位,所以进程是动态的。系统运行一个程序便是一个进程从建立,运行到消亡的过程。java

在 Java 中,当咱们启动 main 函数时其实就是启动了一个 JVM 的进程,而 main 函数所在的线程就是这个进程中的一个线程,也称主线程。缓存

线程

线程与进程类似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程当中能够产生多个线程。与进程不一样的是同类的多个线程共享进程的堆和方法区资源,但每一个线程有本身的程序计数器、虚拟机栈和本地方法栈,因此系统在产生一个线程,或是在各个线程之间做切换工做时,负担要比进程小得多,也正由于如此,线程也被称为轻量级进程。多线程

一个进程中能够有多个线程,多个线程共享进程的堆和方法区 (JDK1.8 以后的元空间)资源,可是每一个线程有本身的程序计数器、虚拟机栈 和 本地方法栈。并发

总结: 线程 是 进程 划分红的更小的运行单位。线程和进程最大的不一样在于基本上各进程是独立的,而各线程则不必定,由于同一进程中的线程极有可能会相互影响。线程执行开销小,但不利于资源的管理和保护;而进程正相反函数

Java线程生命周期和状态

线程死锁

多个线程同时被阻塞,它们中的一个或者所有都在等待某个资源被释放。因为线程被无限期地阻塞,所以程序不可能正常终止。工具

public class DeadLockDemo {
    private static Object resource1 = new Object();//资源 1
    private static Object resource2 = new Object();//资源 2

    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (resource1) {
                System.out.println(Thread.currentThread() + "get resource1");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread() + "waiting get resource2");
                synchronized (resource2) {
                    System.out.println(Thread.currentThread() + "get resource2");
                }
            }
        }, "线程 1").start();

        new Thread(() -> {
            synchronized (resource2) {
                System.out.println(Thread.currentThread() + "get resource2");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread() + "waiting get resource1");
                synchronized (resource1) {
                    System.out.println(Thread.currentThread() + "get resource1");
                }
            }
        }, "线程 2").start();
    }
}

避免线程死锁

咱们只要破坏产生死锁的四个条件中的其中一个就能够了。线程

  • 破坏互斥条件
    这个条件咱们没有办法破坏,由于咱们用锁原本就是想让他们互斥的(临界资源须要互斥访问)。
  • 破坏请求与保持条件
    一次性申请全部的资源。
  • 破坏不剥夺条件
    占用部分资源的线程进一步申请其余资源时,若是申请不到,能够主动释放它占有的资源。
  • 破坏循环等待条件
    靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。

sleep() 方法和 wait() 方法区别和共同点

  • 二者最主要的区别在于:sleep 方法没有释放锁,而 wait 方法释放了锁 。
  • 二者均可以暂停线程的执行。
  • Wait 一般被用于线程间交互/通讯,sleep 一般被用于暂停执行。
  • wait() 方法被调用后,线程不会自动苏醒,须要别的线程调用同一个对象上的 notify() 或者 notifyAll() 方法。sleep() 方法执行完成后,线程会自动苏醒。或者可使用wait(long timeout)超时后线程会自动苏醒。

调用 start 方法方可启动线程并使线程进入就绪状态,而 run 方法只是 thread 的一个普通方法调用,仍是在主线程里执行。code

synchronized关键字最主要的三种使用方式:

  • 修饰实例方法: 做用于当前对象实例加锁,进入同步代码前要得到当前对象实例的锁
  • 修饰静态方法: :也就是给当前类加锁,会做用于类的全部对象实例,由于静态成员不属于任何一个实例对象,是类成员( static 代表这是该类的一个静态资源,无论new了多少个对象,只有一份)。因此若是一个线程A调用一个实例对象的非静态 synchronized 方法,而线程B须要调用这个实例对象所属类的静态 synchronized 方法,是容许的,不会发生互斥现象,由于访问静态 synchronized 方法占用的锁是当前类的锁,而访问非静态 synchronized 方法占用的锁是当前实例对象锁。
  • 修饰代码块: 指定加锁对象,对给定对象加锁,进入同步代码库前要得到给定对象的锁。

总结: synchronized 关键字加到 static 静态方法和 synchronized(class)代码块上都是是给 Class 类上锁。synchronized 关键字加到实例方法上是给对象实例上锁。尽可能不要使用 synchronized(String a) 由于JVM中,字符串常量池具备缓存功能!对象

线程池

线程池提供了一种限制和管理资源(包括执行一个任务)。 每一个线程池还维护一些基本统计信息,例如已完成任务的数量。blog

使用线程池的好处:

  • 下降资源消耗。 经过重复利用已建立的线程下降线程建立和销毁形成的消耗。
  • 提升响应速度。 当任务到达时,任务能够不须要的等到线程建立就能当即执行。
  • 提升线程的可管理性。 线程是稀缺资源,若是无限制的建立,不只会消耗系统资源,还会下降系统的稳定性,使用线程池能够进行统一的分配,调优和监控。

实现Runnable接口和Callable接口的区别

若是想让线程池执行任务的话须要实现的Runnable接口或Callable接口。 Runnable接口或Callable接口实现类均可以被ThreadPoolExecutor或 ScheduledThreadPoolExecutor执行。二者的区别在于 Runnable 接口不会返回结果可是 Callable 接口能够返回结果。

备注: 工具类Executors能够实现Runnable对象和Callable对象之间的相互转换。(Executors.callable(Runnable task)或Executors.callable(Runnable task,Object resule))。

执行execute()方法和submit()方法的区别

  1. execute() 方法用于提交不须要返回值的任务,因此没法判断任务是否被线程池执行成功与否;

  2. submit() 方法用于提交须要返回值的任务。线程池会返回一个Future类型的对象,经过这个Future对象能够判断任务是否执行成功,而且能够经过future的get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,而使用 get(long timeout,TimeUnit unit)方法则会阻塞当前线程一段时间后当即返回,这时候有可能任务没有执行完。

生产者/消费者模式

生产者和消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一个存储空间,生产者往存储空间中添加产品,消费者从存储空间中取走产品,当存储空间为空时,消费者阻塞,当存储空间满时,生产者阻塞。

生产者消费者问题是研究多线程程序时绕不开的经典问题之一,它描述是有一块缓冲区做为仓库,生产者能够将产品放入仓库,消费者则能够从仓库中取走产品。在Java中一共有四种方法支持同步,其中前三个是同步方法,一个是管道方法。

  1. Object的wait() / notify()方法
  2. Lock和Condition的await() / signal()方法
  3. BlockingQueue阻塞队列方法
  4. PipedInputStream / PipedOutputStream

countDownLatch(倒计时门栓)

  • countDownLatch这个类使一个线程等待其余线程各自执行完毕后再执行。
  • 是经过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示全部线程都执行完毕,而后在闭锁上等待的线程就能够恢复工做了。
相关文章
相关标签/搜索