欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java泛型总结

程序员文章站 2022-05-31 18:59:55
Java并发 进程 进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。 在 Java 中,当我们启动 main 函数时其实就是启动了一个 JVM 的进程,而 main 函数所在的线程就是这个进程中的一个线程,也称主线程。 线程 ......

java并发

进程

进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。

在 java 中,当我们启动 main 函数时其实就是启动了一个 jvm 的进程,而 main 函数所在的线程就是这个进程中的一个线程,也称主线程。

线程

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

一个进程中可以有多个线程,多个线程共享进程的堆和方法区 (jdk1.8 之后的元空间)资源,但是每个线程有自己的程序计数器、虚拟机栈 和 本地方法栈。

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

java线程生命周期和状态

Java泛型总结

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 的一个普通方法调用,还是在主线程里执行。

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

  • 修饰实例方法: 作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁
  • 修饰静态方法: :也就是给当前类加锁,会作用于类的所有对象实例,因为静态成员不属于任何一个实例对象,是类成员( static 表明这是该类的一个静态资源,不管new了多少个对象,只有一份)。所以如果一个线程a调用一个实例对象的非静态 synchronized 方法,而线程b需要调用这个实例对象所属类的静态 synchronized 方法,是允许的,不会发生互斥现象,因为访问静态 synchronized 方法占用的锁是当前类的锁,而访问非静态 synchronized 方法占用的锁是当前实例对象锁。
  • 修饰代码块: 指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

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

线程池

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

使用线程池的好处:

  • 降低资源消耗。 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  • 提高响应速度。 当任务到达时,任务可以不需要的等到线程创建就能立即执行。
  • 提高线程的可管理性。 线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

实现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时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。