J2EE 知识点总结_上

news/2024/5/16 23:42:14/文章来源:https://blog.csdn.net/qq_44807756/article/details/127145768

J2EE 知识点总结_上

  • 基础概念
  • 数组
      • 选择排序 :
      • 交换排序 :
      • 插入排序
  • 面向对象
      • 重载(**Overload**)的概念
      • 构造器的作用:
      • JavaBean
      • 多态性
      • instanceof 操作符
      • ==操作符与equals方法:
      • 包装类(Wrapper)的使用
      • 垃圾回收机制关键点
      • 单例(Singleton)设计模式-饿汉式
      • 单例(Singleton)设计模式-懒汉式(线程不安全)
      • 工厂设计模式
  • 异常处理
  • 多线程
      • JDK1.5之前创建新执行线程有两种方法:
      • 线程的生命周期
      • Synchronized的使用方法
      • 同步机制中的锁
      • 单例设计模式之懒汉式(线程安全)
      • Lock(锁)
      • 线程通信
      • JDK5.0新增线程创建方式

申明: 未经许可,禁止以任何形式转载,若要引用,请标注链接地址
全文共计19757字,阅读大概需要5分钟
欢迎关注我的个人公众号:不懂开发的程序猿

基础概念

Java:跨平台的语言(能运行在Windows,Mac OS,Linux等操作系统之上)

Write Once, Run Anywhere!

JVM:跨语言的平台(能为Java,Python,Kotlin等语言编译字节码)

JDK(Java Development Kit Java开发工具包):是提供给Java开发人员使用的,其中包含了java的开发工具,也包括了 JRE。所以安装了JDK,就不用在单独安装JRE了。

JRE(Java Runtime Environment Java运行环境) 包括Java虚拟机(JVM Java Virtual Machine)和Java程序所需的核心类库等, 如果想要运行一个开发好的Java程序,计算机中只需要安装JRE即可。

JDK = JRE + 开发工具集(例如Javac编译工具等)

JRE = JVM + Java SE标准类库

正数的原码、反码、补码都相同

负数的反码:是对原码按位取反(中间态)

负数的补码:其反码加1。计算机底层都是使用的数值的补码保存数据的。

“&”和“&&”的区别:

单&时,左边无论真假,右边都进行运算;

双&时,如果左边为真,右边参与运算,如果左边为假,那么右边不参与运算。

javadoc文档解析

javadoc -encoding UTF-8 -charset UTF-8 -d myhello -author -version HelloWorld.java

java入门问题1——javadoc解析时,cmd窗口报错:编码GBK的不可映射字符
问题1描述:在cmd中运行命令,javadoc -d myhello -author -version HelloJava.java 时,报错编码GBK的不可映射字符。

解决方法:经过网页查询,且前提为使用编码UTF-8(其他编码对应更改就可以),将cmd中的命令改为javadoc -encoding UTF-8 -charset UTF-8 -d myhello -author -version HelloJava.java

上述问题得到解决,但出现了新的问题。

问题2描述:javadoc错误-找不到可以文档化的公共或受保护的类。

解决方法:在源文件中的class前加入public后,可以成功运行。

数组

一维数组的声明方式: type var[] 或 type[] var;

java.util.Arrays常用工具类

boolean equals(int[] a,int[] b) 判断两个数组是否相等。

String toString(int[] a) 输出数组信息。

void fill(int[] a,int val) 将指定值填充到数组之中。

void sort(int[] a) 对数组进行排序。

int binarySearch(int[] a,int key) 对排序后的数组进行二分法检索指定的值。

int[] copyOfRange(int[] original, int from, int to)

选择排序 :

(直接)简单选择排序:

每次都从一组无序数组中选一个最小的,然后和第一个对调。

堆排序:

1)根据初始数组去构造初始堆

1.1)建初始堆的过程:先将一个无序数组按照从上到小,从左到右排列成完全二叉树。

然后再从最后一个非叶子结点开始,从右到左、从下到上调整为大顶堆。

2)交换第一个和最后一个元素,输出最后一个元素(最大值),然后把剩下的元素调整为大顶堆,再交换第一个和最后一个元素,输出最后一个元素再调整剩下的元素为大顶堆,重复这个操作,直至整个数组排序完成。

交换排序 :

冒泡排序

比如说从小到大排序:依次比较相邻的两个元素,大的放右边。重复n-1轮

快速排序

一次划分的过程:有2个指针,low指针指向头元素,high指针指向尾元素,一般取第一个元素为界点(标准)元素,为了减少数据移动,将标准元素暂存在R[0]中(此时的low指针指向空)最后再放入最终位置。开始high指针从后往前移动,找比界点元素小的元素,放在low指针的位置(此时的high指针为空),然后low指针从前往后移动,找比界点元素大的元素,放在high指针的位置。重复上述的过程知道low指针和high指针位置重合,把界点元素放在该位置。

重复划分过程。

插入排序

直接插入排序

从一组无序数组的第2位开始,每次只和它前面的数进行比较,直接插入在正确的位置

Shell排序 (缩小增量排序)

比如有8个元素一组的无序数组,

第一趟(d=4):第5个位置元素和第1个位置元素比大小,小的话就交换位置(大的放后面),第6个位置元素和第2个位置元素比大小,第7个位置元素和第3个位置元素比大小,第8个位置元素和第4个位置元素比大小

第二趟(d=2):第4个位置元素和第2个位置元素比大小,小的话就交换位置(大的放后面),第6个位置元素和第4个位置元素比大小,第8个位置元素和第6个位置元素比大小,大的放后面

然后:第3个位置元素和第1个位置元素比大小,小的话就交换位置(大的放后面),第5个位置元素和第3个位置元素比大小,第7个位置元素和第5个位置元素比大小,大的放后面

第三趟(d=1):后一个位置和前一个位置元素比大小,小的话就交换位置(大的放后面)

折半插入排序

折半插入排序是对直接插入排序的一种改良方式,

二路归并排序

将一组无序数组,分为左右一半,不停的进行递归拆分,递归深度为log2n,直到分为单个元素不可再分为止

我们将单个元素进行排序合成一个有序序列

计数排序

一组有确定范围的无序数组,申请一个大小为arr.length+1长度的数组,下标从arr[0]~arr[length],默认初始化为0,开始计每一个数出现的次数,就在对应下标的数的位置填出现的次数,然后遍历输出这个数组。

桶式排序

是计数排序的升级版。原理是:假设输入的数据服从均匀分布,将数据分配到有限数量的桶中,再对每个桶分别进行排序。分配的原则是利用函数映射关系,整个算法的高效与否就在于这个映射函数的确定。

思路:

1、根据待排序集合中的数据,确定映射规则和桶的数量;

2、遍历待排序数组,将每一个元素根据映射规则,移动到对应的桶里;

3、对每一个桶中的元素进行排序

4、依次输出每个桶中的数据,得到整个有序集合

步骤:

1、找到待排序数组中的最大值amx,和最小值min

2、桶的数量为:(max-min)/ arr.length + 1,桶的下标是从0开始

3、映射函数的对映规则:也就是存放桶的下标=(arr[i] - min)/ arr.length

4、这时候,每个桶里的数据是无序,但是随着桶的下标递增,桶里的数据是递增

5、再对每个桶里的数据进行排序。桶内排序可以选择比较排序或者其他排序

6、遍历桶数组

基数排序

先把无序数组变成位数都一样,不足的高位补0,然后先按照低位排好序,再按照高位排好序,依次类推,直到最高位,最后遍历输出数组就行

十大排序算法的代码具体实现,详情见《数据结构》专栏

面向对象

重载(Overload)的概念

在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数 类型不同即可。

重载的特点: 与返回值类型无关,只看参数列表,且参数列表必须不同。(参数个数或参数类 型)。调用时,根据方法参数列表的不同来区别。

Override重写(覆盖):

表示子类中的方法可以与父类中的某个方法的名称和参数完全相同,通过子类创建的实例对象调用这个方法时,将调用子类中的定义方法(子类重写父类中的方法),是面向对象编程的中多态性的一种表现。

要求:

  1. 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
  2. 子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型
  3. 子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限
    1. 子类不能重写父类中声明为private权限的方法
  4. 子类方法抛出的异常不能大于父类被重写方法的异常

注意: 子类与父类中同名同参数的方法必须同时声明为非static的(即为重写),或者同时声明为 static的(不是重写)。因为static方法是属于类的,子类无法覆盖父类的方法

overwrite重写:

java官方文档没有该词的出现,所以java中就没有它的存在,但是也有人把overwrite解释为override。

在**C++**中将Override和overwrite进行了区分。

Override(覆盖):

是指派生类函数覆盖基类函数,特征是:

(1)不同的范围(分别位于派生类与基类);

(2)函数名字相同;

(3)参数相同;

(4)基类函数必须有virtual 关键字。

Overwrite(重写):

是指派生类的函数屏蔽了与其同名的基类函数,规则如下:

(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏。

(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏。

构造器的作用:

创建对象;

给对象进行初始化

总结:属性赋值过程

赋值的位置: ① 默认初始化 ② 显式初始化 ③ 构造器中初始化 ④ 通过“对象.属性“或“对象.方法”的方式赋值

赋值的先后顺序: ① - ② - ③ - ④

JavaBean

JavaBean是一种Java语言写成的可重用组件。

所谓JavaBean,是指符合如下标准的Java类:

类是公共的

有一个无参的公共的构造器

有属性,且有对应的get、set方法

用户可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以 用Java代码创造的对象进行打包,并且其他的开发者可以通过内部的JSP 页面、Servlet、其他JavaBean、applet程序或者应用来使用这些对象。用 户可以认为JavaBean提供了一种随时随地的复制和粘贴的功能,而不用关 心任何改变。

package com.jerry.java;/*** @author jerry_jy* @create 2022-09-27 10:32*/
public class JavaBean {//类是公共的private String name;//有属性,且有对应的get、set方法private int age;public JavaBean(){//有一个无参的公共的构造器}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}

在这里插入图片描述

对于class的权限修饰只可以用public和default(缺省)。

​ public类可以在任意地方被访问。

​ default类只可以被同一个包内部的类访问。

多态性

是面向对象中最重要的概念,在Java中的体现: 对象的多态性:父类的引用指向子类的对象

Person p = new Student(); //Person类型的变量p,指向Student类型的对象

Object o = new Person();//Object类型的变量o,指向Person类型的对象

o = new Student(); //Object类型的变量o,指向Student类型的对象

子类可看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向上转型(upcasting)

一个引用类型变量如果声明为父类的类型,但实际引用的是子类 对象,那么该变量就不能再访问子类中添加的属性和方法

Student m = new Student();

m.school = “pku”; //合法,Student类有school成员变量

Person e = new Student();

e.school = “pku”; //非法,Person类没有school成员变量

属性是在编译时确定的,编译时e为Person类型,没有school成员变量,因而编译错误。

虚拟方法调用(多态情况下)(Virtual Method Invocation)

子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父 类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。

instanceof 操作符

x instanceof A:检验x是否为类A的对象,返回值为boolean型。

要求x所属的类与类A必须是子类和父类的关系,否则编译错误。

如果x属于类A的子类B,x instanceof A值也为true。

Object类是所有Java类的根父类

如果在类的声明中未使用extends关键字指明其父类,则默认父类 为java.lang.Object类

public class Person { … } 等价于: public class Person extends Object { … }

例:method(Object obj){…} //可以接收任何类作为其参数

Person o=new Person();

method(o);

==操作符与equals方法:

==操作符

基本类型比较值:只要两个变量的值相等,即为true。

引用类型比较引用(是否指向同一个对象):只有指向同一个对象时,==才返回true。

用“==”进行比较时,符号两边的数据类型必须兼容(可自动转换的基本 数据类型除外),否则编译出错

equals():所有类都继承了Object,也就获得了equals()方法。还可以重写。

只能比较引用类型,其作用与“==”相同,比较是否指向同一个对象。

格式:obj1.equals(obj2)

特例:当用equals()方法进行比较时,对类File、String、Date及包装类 (Wrapper Class)来说,是比较类型及内容而不考虑引用的是否是同一个对 象;原因:在这些类中重写了Object类的equals()方法。

面试题:==和equals的区别

1 == 既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型 就是比较内存地址

2 equals的话,它是属于java.lang.Object类里面的方法,如果该方法没有被重写过默认也 是==;我们可以看到String等类的equals方法是被重写过的,而且String类在日常开发中 用的比较多,久而久之,形成了equals是比较值的错误观点。

3 具体要看自定义类里有没有重写Object的equals方法来判断。

4 通常情况下,重写equals方法,会比较类中的相应属性是否都相等。

toString() 方法

toString()方法在Object类中定义,其返回值是String类型,返回类名和它 的引用地址。

在进行String与其它类型数据的连接操作时,自动调用toString()方法

包装类(Wrapper)的使用

在这里插入图片描述

在这里插入图片描述

代码演示:

        // 包装类的使用int i = 5;Integer integer = new Integer(i);//把基本数据类型转化为包装类,调用new Integer()System.out.println(integer.getClass().getName());//java.lang.Integerint i1 = integer.intValue();//包装类转化为基本数据类型,调用包装类的方法:xxxValue()System.out.println(i1);//5int j = 1;String s = String.valueOf(j);// 基本数据类型转化为String类,方式一:调用String.valueOf()System.out.println(s.getClass().getName());//java.lang.StringString s1 = j + ""; // 基本数据类型转化为String类,方式二:+ ""System.out.println(s1.getClass().getName());//java.lang.StringString s2 = "123";int i2 = Integer.parseInt(s2); // String类转化为基本数据类型,调用Integer.parseInt()System.out.println(i2);//123Integer integer1 = new Integer(2);String s3 = integer1.toString();// 包装类转化为String类,调用包装类对象的toString()方法System.out.println(s3.getClass().getName());//java.lang.StringInteger i3 = new Integer("456");// String类转化为包装类System.out.println(i3.getClass().getName()); //java.lang.Integer

垃圾回收机制关键点

垃圾回收机制只回收JVM堆内存里的对象空间。

对其他物理连接,比如数据库连接、输入流输出流、Socket连接无能为力

现在的JVM有多种垃圾回收实现算法,表现各异。

垃圾回收发生具有不可预知性,程序无法精确控制垃圾回收机制执行。

可以将对象的引用变量设置为null,暗示垃圾回收机制可以回收该对象。

程序员可以通过System.gc()或者Runtime.getRuntime().gc()来通知系统进行垃圾回收,会有
一些效果,但是系统是否进行垃圾回收依然不确定。

垃圾回收机制回收任何对象之前,总会先调用它的finalize方法(如果覆盖该方法,让一
个新的引用变量重新引用该对象,则会重新激活对象)。

永远不要主动调用某个对象的finalize方法,应该交给垃圾回收机制调用。

如果想让一个类的所有实例共享数据,就用类变量!

因为不需要实例就可以访问static方法,因此static方法内部不能有this。(也 不能有super ? YES!)

static修饰的方法不能被重写

单例(Singleton)设计模式-饿汉式

class Singleton {// 1.私有化构造器private Singleton() {}// 2.内部提供一个当前类的实例// 4.此实例也必须静态化private static Singleton single = new Singleton();// 3.提供公共的静态的方法,返回当前类的对象public static Singleton getInstance() {return single;}
}

单例(Singleton)设计模式-懒汉式(线程不安全)

class Singleton {// 1.私有化构造器private Singleton() {}// 2.内部提供一个当前类的实例// 4.此实例也必须静态化private static Singleton single;// 3.提供公共的静态的方法,返回当前类的对象public static Singleton getInstance() {if(single == null) {single = new Singleton();}return single;}
}

工厂设计模式

interface Car {void run();
}
class Audi implements Car {public void run() {System.out.println("奥迪在跑");}
}
class BYD implements Car {public void run() {System.out.println("比亚迪在跑");}
}
//工厂类
class CarFactory {//方式一public static Car getCar(String type) {if ("奥迪".equals(type)) {return new Audi();} else if ("比亚迪".equals(type)) {return new BYD();} else {return null;}}
//方式二
// public static Car getAudi() {
// return new Audi();
// }
//
// public static Car getByd() {
// return new BYD();
// }
}
public class Client02 {public static void main(String[] args) {Car a = CarFactory.getCar("奥迪");a.run();Car b = CarFactory.getCar("比亚迪");b.run();}
}

对main方法的语法理解:

public static void main(String[] args) {}

由于Java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是 public,又因为Java虚拟机在执行main()方法时不必创建对象,所以该方法必须 是static的,该方法接收一个String类型的数组参数,该数组中保存执行Java命令 时传递给所运行的类的参数。

抽象方法:只有方法的声明,没有方法的实现。以分号结束:

比如:public abstract void talk();

接口(interface)是抽象方法和常量值定义的集合

接口中的所有成员变量都默认是由public static final修饰的。

接口中的所有抽象方法都默认是由public abstract修饰的。

Java 8中,你可以为接口添加静态方法和默认方法。

静态方法:使用 static 关键字修饰。可以通过接口直接调用静态方法,

默认方法:默认方法使用 default 关键字修饰。可以通过实现类对象来调用。

异常处理

详情见博客《Java2EE练习及面试题_chapter07异常处理》

多线程

JDK1.5之前创建新执行线程有两种方法:

继承Thread类的方式

  1. 定义子类继承Thread类。
  2. 子类中重写Thread类中的run方法。
  3. 创建Thread子类对象,即创建了线程对象。
  4. 调用线程对象start方法:启动线程,调用run方法。

代码演示:

package com.jerry.java;/*** @author jerry_jy* @create 2022-10-01 17:33*/
public class ThreadTest {public static void main(String[] args) {//3.创建Thread子类对象,即创建了线程对象。MyThread myThread = new MyThread();//4.调用线程对象start方法:启动线程,调用run方法。myThread.start();}
}
//1.定义子类继承Thread类。
class MyThread extends Thread {public MyThread() {super();}
// 2.子类中重写Thread类中的run方法。@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println("子线程:" + i);}}
}

注意点:

  1. 如果自己手动调用run()方法,那么就只是普通方法,没有启动多线程模式。
  2. run()方法由JVM调用,什么时候调用,执行的过程控制都有操作系统的CPU 调度决定。
  3. 想要启动多线程,必须调用start方法。
  4. 一个线程对象只能调用一次start()方法启动,如果重复调用了,则将抛出以上 的异常“IllegalThreadStateException”。

实现Runnable接口的方式

  1. 定义子类,实现Runnable接口。
  2. 子类中重写Runnable接口中的run方法。
  3. 通过Thread类含参构造器创建线程对象。
  4. 将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
  5. 调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。

代码演示:

package com.jerry.java;/*** @author jerry_jy* @create 2022-10-01 17:40*/
public class RunnableTest {public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();//3.通过Thread类含参构造器创建线程对象。//4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。Thread thread = new Thread(myRunnable);//5.调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。thread.start();}
}//1.定义子类,实现Runnable接口。
class MyRunnable implements Runnable{//2.子类中重写Runnable接口中的run方法。@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println("子线程:"+i);}}
}

多线程的创建–>继承方式和实现方式的联系与区别:

区别

​ 继承Thread:线程代码存放Thread子类run方法中。

​ 实现Runnable:线程代码存在接口的子类的run方法。

实现方式的好处

​ 避免了单继承的局限性

​ 多个线程可以共享同一个接口实现类的对象,非常适合多个相同线 程来处理同一份资源。

Thread类的有关方法

void start(): 启动线程,并执行对象的run()方法
run(): 线程在被调度时执行的操作
String getName(): 返回线程的名称
void setName(String name):设置该线程名称
static Thread currentThread(): 返回当前线程。在Thread子类中就是this,通常用于主线程和Runnable实现类
static void yield():线程让步暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程若队列中没有同优先级的线程,忽略此方法
join() :当某个程序执行流中调用其他线程的 join() 方法时,调用线程将被阻塞,直到 join() 方法加入的 join 线程执行完为止低优先级的线程也可以获得执行
static void sleep(long millis)(指定时间:毫秒)令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队。抛出InterruptedException异常
stop(): 强制线程生命期结束,不推荐使用
boolean isAlive():返回boolean,判断线程是否还活着

线程的优先级

线程的优先级等级MAX_PRIORITY:10 MIN _PRIORITY:1 NORM_PRIORITY:5 
涉及的方法getPriority() :返回线程优先值setPriority(int newPriority) :改变线程的优先级
说明线程创建时继承父线程的优先级低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用

补充:线程的分类

Java中的线程分为两类:一种是守护线程,一种是用户线程

​ 它们在几乎每个方面都是相同的,唯一的区别是判断JVM何时离开

​ 守护线程是用来服务用户线程的,通过在start()方法前调用 thread.setDaemon(true)可以把一个用户线程变成一个守护线程

​ Java垃圾回收就是一个典型的守护线程。

​ 若JVM中都是守护线程,当前JVM将退出。

线程的生命周期

JDK中用Thread.State类定义了线程的几种状态

要想实现多线程,必须在主线程中创建新的线程对象。Java语言使用Thread类 及其子类的对象来表示线程,在它的一个完整的生命周期中通常要经历如下的五 种状态:

​ 新建: 当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建 状态

​ 就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已 具备了运行的条件,只是没分配到CPU资源

​ 运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态, run()方法定义了线 程的操作和功能

​ 阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出 CPU 并临时中 止自己的执行,进入阻塞状态

​ 死亡:线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束

在这里插入图片描述

在这里插入图片描述

Synchronized的使用方法

Java对于多线程的安全问题提供了专业的解决方式:同步机制

1. 同步代码块:
synchronized (对象){
// 需要被同步的代码;
}
2. synchronized还可以放在方法声明中,表示整个方法为同步方法。
例如:
public synchronized void show (String name){.
}

在这里插入图片描述

同步机制中的锁

同步锁机制:

在《Thinking in Java》中,是这么说的:对于并发工作,你需要某种方式来防 止两个任务访问相同的资源(其实就是共享资源竞争)。 防止这种冲突的方法 就是当资源被一个任务使用时,在其上加锁。第一个访问某项资源的任务必须 锁定这项资源,使其他任务在其被解锁之前,就无法访问它了,而在其被解锁 之时,另一个任务就可以锁定并使用它了。

synchronized的锁是什么?

​ 任意对象都可以作为同步锁。所有对象都自动含有单一的锁(监视器)

​ 同步方法的锁:静态方法(类名.class)、非静态方法(this)

​ 同步代码块:自己指定,很多时候也是指定为this或类名.class

单例设计模式之懒汉式(线程安全)

package com.jerry.java;/*** @author jerry_jy* @create 2022-10-01 19:27*/
public class SingletonTest {//    单例设计模式之懒汉式(线程安全)public static void main(String[] args) {Singleton singleton1 = Singleton.getInstance();Singleton singleton2 = Singleton.getInstance();System.out.println(singleton1 == singleton2);//true}
}class Singleton {// 2.内部提供一个当前类Singleton的实例singleton// 4.此实例也必须静态化staticprivate static Singleton singleton;// 1.私有化构造器private Singleton() {}// 3.提供公共的静态的方法getInstance,返回当前类的对象return singletonpublic static Singleton getInstance() {if (singleton == null) {//synchronized同步监视器:每次只new 一个当前类的对象synchronized (Singleton.class) {if (singleton == null) {singleton = new Singleton();}}}return singleton;}
}

Lock(锁)

class A{private final ReentrantLock lock = new ReenTrantLock();public void m(){lock.lock();try{//保证线程安全的代码;}finally{lock.unlock();}}
}

注意:如果同步代码有异常,要将unlock()写入finally语句块

synchronized 与 Lock 的对比

  1. Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是 隐式锁,出了作用域自动释放

  2. Lock只有代码块锁,synchronized有代码块锁和方法锁

  3. 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有 更好的扩展性(提供更多的子类)

优先使用顺序:

Lock --> 同步代码块(已经进入了方法体,分配了相应资源)–> 同步方法 (在方法体之外)

线程通信

wait() 与 notify() 和 notifyAll()

​ wait():令当前线程挂起并放弃CPU、同步资源并等待,使别的线程可访问并修改共享资源,而当 前线程排队等候其他线程调用notify()或notifyAll()方法唤醒,唤醒后等待重新获得对监视器的所有权后才能继续执行。

​ notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待

​ notifyAll ():唤醒正在排队等待资源的所有线程结束等待.

这三个方法只有在synchronized方法或synchronized代码块中才能使用,否则会报 java.lang.IllegalMonitorStateException异常。

因为这三个方法必须有锁对象调用,而任意对象都可以作为synchronized的同步锁, 因此这三个方法只能在Object类中声明。

wait() 方法

​ 在当前线程中调用方法: 对象名.wait()

​ 使当前线程进入等待(某对象)状态 ,直到另一线程对该对象发出 notify (或notifyAll) 为止。

​ 调用方法的必要条件:当前线程必须具有对该对象的监控权(加锁)

​ 调用此方法后,当前线程将释放对象监控权 ,然后进入等待

​ 在当前线程被notify后,要重新获得监控权,然后从断点处继续代码的执行。

notify()/notifyAll()

​ 在当前线程中调用方法: 对象名.notify()

​ 功能:唤醒等待该对象监控权的一个/所有线程

​ 调用方法的必要条件:当前线程必须具有对该对象的监控权(加锁)

示例题目:使用两个线程打印 1-100。线程1, 线程2 交替打印

package com.jerry.java;/*** @author jerry_jy* @create 2022-10-02 17:26*/
public class Communication implements Runnable {int i = 1;@Overridepublic void run() {while (true) {synchronized (this) {notify();if (i <= 100) {try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + ":" + i);i++;try {//使得调用如下wait()方法的线程进入阻塞状态wait();} catch (InterruptedException e) {e.printStackTrace();}} else {break;}}}}public static void main(String[] args) {Communication communication = new Communication();Thread thread1 = new Thread(communication);Thread thread2 = new Thread(communication);thread1.setName("线程一");thread2.setName("线程二");thread1.start();thread2.start();}
}

经典例题:生产者/消费者问题

package com.jerry.java;/*** @author jerry_jy* @create 2022-10-02 17:45*/
public class ProductTest {/*生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。这里可能出现两个问题:生产者比消费者快时,消费者会漏掉一些数据没有取到。消费者比生产者快时,消费者会取相同的数据。*/public static void main(String[] args) {Clerk clerk = new Clerk();Productor productor = new Productor(clerk);Consumer consumer = new Consumer(clerk);Thread thread1 = new Thread(productor);Thread thread2 = new Thread(consumer);thread1.start();thread2.start();}
}class Clerk {private int product = 0;public synchronized void addProduct() {if (product > 20) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}} else {product++;System.out.println("生产者生产了第" + product + "个产品");notifyAll();}}public synchronized void getProduct() {if (product <= 0) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}} else {System.out.println("消费者取走了第" + product + "个产品");product--;notifyAll();}}}class Productor implements Runnable {Clerk clerk;public Productor(Clerk clerk) {this.clerk = clerk;}@Overridepublic void run() {System.out.println("生产者开始生产产品");while (true) {try {Thread.sleep((int) Math.random() * 1000);} catch (InterruptedException e) {e.printStackTrace();}clerk.addProduct();}}
}class Consumer implements Runnable {Clerk clerk;public Consumer(Clerk clerk) {this.clerk = clerk;}@Overridepublic void run() {while (true) {try {Thread.sleep((int) Math.random() * 1000);} catch (InterruptedException e) {e.printStackTrace();}clerk.getProduct();}}
}

JDK5.0新增线程创建方式

新增方式一:实现Callable接口

与使用Runnable相比, Callable功能更强大些

​ 相比run()方法,可以有返回值

​ 方法可以抛出异常

​ 支持泛型的返回值

​ 需要借助FutureTask类,比如获取返回结果

package com.jerry.java;import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;/*** @author jerry_jy* @create 2022-10-02 18:46*/
public class CallableTest {public static void main(String[] args) {//3.创建Callable接口实现类的对象MyCallable myCallable = new MyCallable();//4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象FutureTask futureTask = new FutureTask<>(myCallable);//5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()Thread thread = new Thread(futureTask);thread.start();//6.获取Callable中call方法的返回值//get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。try {Object o = futureTask.get();System.out.println("总和为:" + o);} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}
}//1.创建一个实现Callable的实现类
class MyCallable implements Callable {//2.实现call方法,将此线程需要执行的操作声明在call()中@Overridepublic Object call() throws Exception {int sum = 0;for (int i = 1; i <= 100; i++) {sum += i;}return sum;}
}

新增方式二:使用线程池

JDK 5.0起提供了线程池相关API:ExecutorService 和 Executors

package com.jerry.java;import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;/*** @author jerry_jy* @create 2022-10-02 18:54*/
public class ThreadPool {public static void main(String[] args) {ExecutorService service = Executors.newFixedThreadPool(10);ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;//设置线程池的属性System.out.println(service.getClass());//class java.util.concurrent.ThreadPoolExecutorservice1.setCorePoolSize(15);System.out.println(service1.getMaximumPoolSize());//10//2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象service.execute(new NumberThread());//适合适用于Runnableservice.execute(new NumberThread1());//适合适用于Runnable//3.关闭连接池service.shutdown();}
}class NumberThread implements Runnable {@Overridepublic void run() {for (int i = 0; i <= 100; i++) {if (i % 2 == 0) {System.out.println(Thread.currentThread().getName() + ": " + i);}}}
}class NumberThread1 implements Runnable {@Overridepublic void run() {for (int i = 0; i <= 100; i++) {if (i % 2 != 0) {System.out.println(Thread.currentThread().getName() + ": " + i);}}}
}

l {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
//设置线程池的属性
System.out.println(service.getClass());//class java.util.concurrent.ThreadPoolExecutor
service1.setCorePoolSize(15);
System.out.println(service1.getMaximumPoolSize());//10

    //2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象service.execute(new NumberThread());//适合适用于Runnableservice.execute(new NumberThread1());//适合适用于Runnable//3.关闭连接池service.shutdown();
}

}

class NumberThread implements Runnable {

@Override
public void run() {for (int i = 0; i <= 100; i++) {if (i % 2 == 0) {System.out.println(Thread.currentThread().getName() + ": " + i);}}
}

}

class NumberThread1 implements Runnable {

@Override
public void run() {for (int i = 0; i <= 100; i++) {if (i % 2 != 0) {System.out.println(Thread.currentThread().getName() + ": " + i);}}
}

}

–end–

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_17622.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RLE算法机制、缺点及哈夫曼算法和莫尔斯编码

CSDN话题挑战赛第2期 参赛话题&#xff1a;学习笔记 目录 一、RLE算法机制 二、RLE算法的缺点 三、哈夫曼算法和莫尔斯编码 一、RLE算法机制 对 AAAAAABBCDDEEEEEF 这17个半角字符的文件&#xff08;文本文件&#xff09;进行压缩。虽然这些文字没有什么实际意义&#xff0…

Spring源码分析(三)Bean生命周期源码解析1:扫描生成BeanDefinition

Spring最重要的功能就是帮助程序员创建对象&#xff08;也就是IOC&#xff09;&#xff0c;而启动Spring就是为创建Bean对象做准备&#xff0c;如果先分析Spring启动过程的源码&#xff0c;会比较难理解&#xff0c;因为你可能不知道为什么要做这些准备动作&#xff0c;所以我们…

Shiro知识总结二

3. 与 Spring Boot 整合 3.1 框架整合 依赖 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>o…

java基于springboot+vue+nodejs的高校学生健康档案管理系统 element

随着信息技术和网络技术的飞速发展,人类已进入全新信息化时代,传统管理技术已无法高效,便捷地管理信息。为了迎合时代需求,优化管理效率,各种各样的管理系统应运而生,各行各业相继进入信息管理时代,高校学生健康档案管理系统就是信息时代变革中的产物之一。 在经济快速发展的带…

快速玩转Yolov5目标检测—没有好的显卡也能玩(二)

上篇 快速玩转Yolov5目标检测—没有好的显卡也能玩&#xff08;一&#xff09; 已经将YoloV5在我的笔记本电脑上快速跑起来了&#xff0c;因为电脑显卡一般&#xff0c;所以运行的CPU版本&#xff0c;从推理结果来看&#xff0c;耗时还是蛮高的&#xff0c;如下图&#xff0c;…

03 NLP-神经网络基础常识复习2-计算图(乘法节点,分支节点,Repeat节点,Sum节点,MatMul节点)

下面&#xff0c;我们将研究误差反向传播法。不过在此之前&#xff0c;作为准备工作&#xff0c;我们先来介绍一下计算图的相关内容。计算图是计算过程的图形表示。所示为计算图的一个例子 计算图通过节点和箭头来表示。这里&#xff0c;“”表示加法&#xff0c;变量x和y写在各…

【流放之路闪电打击开荒攻略】

重点1&#xff1a;每次攻击杀1群白怪 重点2&#xff1a;地图区域等级-4《角色等级《地图区域等级2 重点3&#xff1a;非boss战斗不死亡 重点4&#xff1a;对下阶段成长有目标&#xff0c;搜集装备 国际服网址 G&#xff08;green&#xff09;R&#xff08;red&#xff09;B&am…

SSTI基础知识

我们用如下环境进行讲解(flask-jinja2):from flask import Flask from flask import render_template from flask import request from flask import render_template_string app = Flask(__name__) @app.route(/) def index():code = request.args.get(id)template = <h3&…

【Pandas总结】第九节 Pandas_累计与分组 pd.groupby()

文章目录一、数据准备二、累计值计算2.1 df.describe()2.2 常用统计值三、分组 pd.groupby()四、更多的使用方法aggregate(),filter(),transform(),apply()4.1 aggregate()4.2 filter()4.3 transform()4.4 apply()在对较大数据进行分析时&#xff0c;有一项最基本的工作就是&am…

2022-09-18-事务机制与锁

事务机制与锁 事务ACID特性(4大特性):原子性;一致性;隔离性;持久性。事务隔离性(四大隔离级别):读未提交;读已提交;可重复读;串行。脏读:读到了别的事务还没有提交,可能随时会被回滚掉的,有可能不存在的数据,这叫做脏读。 可重复读:我第一次查到的数据,我之后…

【选择】选择排序、堆排序(大根堆【升序】,小根堆【降序】)

简单选择排序 思想&#xff1a;默认0号位&#xff0c;定义为min&#xff0c;再从第二位起&#xff0c;遍历所有&#xff0c;找到一个更小的&#xff0c;把下标赋给min&#xff0c;遍历结束&#xff0c;如果当前i下标的值不是min&#xff0c;则说明min更新&#xff0c;有更小的…

【牛客-算法】 NC48 在旋转过的有序数组中寻找目标值

文章目录&#x1f6a9; 前言1.题目描述2.算法设计思路3.算法实现bug记录&#x1f9ed; 遇到问题&#xff08;可跳过&#xff09;&#x1f33b; 写在前面我最初的通过代码&#xff08;C语言&#xff09;4.运行结果5.小结&#x1f525; 该专栏作为算法题笔记&#xff0c;记录算法…

Bert在fine-tune训练时的技巧:①冻结部分层参数、②weight-decay (L2正则化)、③warmup_proportion、④

作为一个NLPer&#xff0c;bert应该是会经常用到的一个模型了。但bert可调参数很多&#xff0c;一些技巧也很多&#xff0c;比如加上weight-decay, layer初始化、冻结参数、只优化部分层参数等等&#xff0c;方法太多了&#xff0c;每次都会纠结该怎么样去finetune&#xff0c;…

打印数组的所有子集

打印数组的所有子集 作者&#xff1a;Grey 原文地址&#xff1a; 博客园&#xff1a;打印数组的所有子集 CSDN&#xff1a;打印数组的所有子集 无重复值情况 题目描述见: LeetCode 78. Subsets 主要思路 定义递归函数 void p(int[] arr, int i, LinkedList<Integer…

【数据结构与算法】深度理解队列(上)

✨hello&#xff0c;进来的小伙伴们&#xff0c;你们好耶&#xff01;✨ &#x1f68e;&#x1f68e;系列专栏&#xff1a;【数据结构与算法】 &#x1f680;&#x1f680;本篇内容:队列从0到1的学习&#xff01; ⛵⛵作者简介&#xff1a;一名双非本科大三在读的科班Java编程小…

11-二叉树-删除

delete(ElementType e)&#xff1a;删除某个值为 e 的结点。实现方法有多种。 按添加结点的规则&#xff0c;小于根结点的放在左边&#xff0c;大于等于根结点的放在右边。b 小于 c 中任意一个子结点&#xff0c;只能放在 c 中最小的一个结点 e 的左子结点下。 除 e 外&#x…

Git基础操作

拉取代码直接clone,复制远程仓库文件夹 git clone git@gitee.com:chen-LinQiang/my-notes.git 在已有仓库文件夹中拉代码 # 初始化 git init # 关联远程仓库 git remote add origin git@gitee.com:chen-LinQiang/my-notes.git # 切换到本地主分支 git checkout master # 若报错…

SpringBoot员工管理的项目——SpringBoot首页定制的操作和国际编码操作(课时十五)

SpringBoot员工管理的项目——SpringBoot后台数据库的搭建(课时十四)_星辰镜的博客-CSDN博客 上篇文章的的文章路径 读者可以回看 有些内容在这里不在说明 本博文完成的两个功能: 利用 Thymeleaf模板引擎完成员工管理系统的的首页定制 国际化编码格式操作 <!--Thymeleaf 说…

计算机网络——媒体接入控制

&#x1f49f;&#x1f49f;前言 ​ 友友们大家好&#xff0c;我是你们的小王同学&#x1f617;&#x1f617; 今天给大家打来的是 计算机网络——媒体接入控制 希望能给大家带来有用的知识 觉得小王写的不错的话麻烦动动小手 点赞&#x1f44d; 收藏⭐ 评论&#x1f4c4; 小王…

20、DQL(编写顺序和执行顺序)

CSDN话题挑战赛第2期 参赛话题&#xff1a;学习笔记 DQL&#xff08;编写顺序和执行顺序&#xff09; 执行顺序&#xff1a; 1、from&#xff08;from 查什么表是第一&#xff09; 2、where 3、group by 和 having 4、select 5、order by&#xff08;你很与众不同哈&…