多线程实例:阻塞队列
阻塞队列是一种具有阻塞功能的队列,满足队列“先进先出”的特点,是一种线性安全的数据结构。当队列为空时,执行出队操作会进行阻塞,直到队列中有元素为止;当队列已经满了,执行入堆操作会进行阻塞,知道队列有空间为止。
阻塞队列的一个典型应用常见就是“生产者消费者”模型。毫无疑问,该模型有两个主体:生产者和消费者。生产者线程负责生产产品,将生产好的产品放进阻塞队列。消费者线程负责消费产品,直接从阻塞队列取产品。
生产者消费者模型具有解耦、平衡速度差异的特点。
解耦:生产者无需关注是谁在消费产品,消费了多少产品,只需关注生产操作即可;消费者无需关注是谁在生产产品,生产了多少产品,只需关注消费操作即可。
平衡速度差异:设想11.11,大量用户向服务器发送了大量的支付请求,一次性将这些请求交给服务器处理,服务器可能会hole不住,因此将这些请求都放进阻塞队列,消费者线程从阻塞队列一个一个地来处理请求就好了。这样就起到了“削峰填谷”的作用,平衡了生产者和消费者之间的速度差异。
标准库中的阻塞队列:
123456public static void main(String[] args) throws I ...
Java线程的创建及状态
什么是线程
在计算机中,一个任务就是一个进程。打开任务管理器,我们可以看见多个进程。如浏览器,QQ。线程是进程的再划分,是进程的一部分。一个进程可以只有一个线程(主线程),也可以有多个线程。拿浏览器举例,打开浏览器的多个网页,一个网页在播放音乐,一个网页在播放视频,一个网页在浏览博客。每个网页有不同的分工,这些浏览器的每个子任务就是一个线程。
线程是更轻量的进程,创建一个线程的消耗要低于创建一个进程,且同一进程的不同线程之间可以共享资源,效率更高。通过多线程可以更好地实现并发编程。
进程vs线程
进程包含线程,进程是线程的一部分,一个进程中至少包含一个线程。
进程与进程之间不能共享资源,同一个进程的线程之间可以共享资源。
进程是系统分配资源的最小单位,线程是系统调度执行的最小单位。
多进程稳定性高于多线程,一个进程的崩溃不会影响其他线程;多线程下一个线程的崩溃可能影响该进程。
创建线程
一个线程至少包含一个进程,main()就是一个线程,我们还可以在里面创建多个线程。
1.继承Thread类,重写run()方法
12345678910111213public class Main ...
线程安全及解决方案
安全,已经成为非常重要的社会话题。所谓“安全第一”,“安全无小事”(手动滑稽),同样,多线程中,线程安全也是非常重要的话题。那么是什么原因造成了线程不安全,又如何解决线程不安全呢?
造成线程不安全的原因
线程的调度
各线程之间是抢占式执行的,线程的执行顺序是随机的,因此可能会产生各种问题。现在最流行什么?做核酸!如果做核酸是抢占式的,做核酸没有一个顺序,做核酸顺序完全靠运气,这能安全吗!
多线程同时修改同一变量
如果是多线程同时读取同一变量,不涉及修改的操作,是线程安全的。但如果多线程同时修改同一变量,这能不乱吗?想当年家里买了一台电脑,我姐姐要用电脑玩QQ炫舞,我要用电脑玩穿越火线,你争我抢,打的是不可开交。
操作指令不是原子的
例如一条加法指令,其实要执行三条指令,load、add、save,先将内存中的变量加载到寄存器,在寄存器中完成加法操作,再将结果写会内存中。假设线程1完成了load、add操作,线程2完成了load、add、save操作,当线程1再去执行save操作,便将线程2的操作覆盖了,线程2说:线程1真是一个猪队友。
内存可见性
线程1循环进行读操作, ...
Java单例模式:懒汉模式和饿汉模式
单例模式,就是一个类中只有一个实例。主要有懒汉模式和饿汉模式。
饿汉模式是在类加载的同时就创建实例,而懒汉模式是使用时才创建实例。
饿汉模式
12345678910class Singleton{ //1.使用static创建一个实例,并且立即实例化 private static Singleton instance = new Singleton(); //2.为了防止在其他地方不小心new这个Singleton,把方法构造为private private Singleton(){} //3.提供一个方法,让外面能够拿到唯一实例 public static Singleton getInstance() { return instance; }}
在多线程中,多个线程对同一数据进行读操作,线程安全。
懒汉模式
12345678910111213class Singleton{ //1.不立即初始化实例,使用时再初始化 private static ...
【JavaWeb】Thymeleaf模板引擎
上篇博客中,我们了解了服务器渲染,并用Thymeleaf模板引擎写了一个简单的猜数字小游戏。
下面我们简单了解一下Thymeleaf的语法。
Thymeleaf常见命令
命令
功能
th:text
在标签体中展示表达式求值结果的文本内容
th:[HTML标签属性]
设置任意的 HTML 标签属性的值
th:if
当表达式的结果为真时则显示内容,否则不显示
th:each
循环访问元素
Thymeleaf语法有很多,我们这里先简单介绍这四个。th:text和th:if在猜数字小游戏案例已经使用过,这里不再介绍。
th:[属性]
HTML模板代码:
1234<body> <a th:href="${url1}">胖虎同学</a> <a th:href="${url2}">胖虎同学1</a></body>
Servlet代码:
12345678910111213141516171819202 ...
【JavaWeb】服务器渲染
页面渲染,所谓渲染就是将数据和页面相结合,根据用户传送的数据不同,页面渲染后的内容也不同。页面渲染可以在服务器端完成,也可以在客户端完成。服务器渲染是将用户传来的数据在服务器端拼接成HTML后传给客户端,而客户端渲染是在客户端将服务器传来的数据拼接成HTML。
服务器渲染
这篇博客主要介绍一下服务器渲染。
我们可以用字符串拼接的方式,将数据拼接成HTML页面。下面我们用字符串拼接的方式完成一个猜数字web版的小游戏。
首先约定好交互方式。
GET /guess
通过get请求,从服务器得到一个页面响应,同时在服务器生成一个1-100的随机数。
POST /guess
通过post请求,将用户输入的数提交到服务器,在服务器比较用户输入的数和随机数的大小,将比较结果和次数返回。
约定好交互方式后,我们创建一个GuessNumServlet类,关联到/guess路径,用doGet方法实现第一个交互接口,doPost方法实现第二个交互接口。代码如下所示:
12345678910111213141516171819202122232425262728293031323334353637 ...
【JavaWeb】Servlet上传文件
Servlet支持上传文件操作,在HttpServletRequest类中,通过Part对象的getPart方法便可以获取请求中指定name的文件。
上传文件分为两个部分,前端部分和后端部分,关于文件操作,这两个部分都很简单,代码量很少。
各文件路径如下:
前端页面:创建一个html文件,通过form表单的action属性,与后端Servlet相关联,method属性指定方法,上传文件通常为post,通过enctype属性指定编码方式,上传文件为multipart/form-data。
1234<form action="file" method="post" enctype="multipart/form-data"> <input type="file" name="myFile"> <input type="submit" value="提交"></form>
后端部分:创建一个上传文件的类U ...
时间复杂度和空间复杂度
如何衡量一个算法的好坏?我们可以从时间和空间两个方面入手,也就是时间复杂度和空间复杂度。
无论是时间复杂度还是空间复杂度,都采用大O的渐进表示法。只保留最高阶项,且最高阶项的系数为1.例如一个算法的执行次数是2N^2+M+3,那么该算法的时间复杂度是O(N^2)。
时间复杂度
时间复杂度的衡量标准也就是算法的执行次数。我们下面用几段代码来练习一下算法时间复杂度的计算。
冒泡排序的时间复杂度
123456789101112131415void bubbleSort(int[] array) { for (int end = array.length; end > 0; end--) { boolean sorted = true; for (int i = 1; i < end; i++) { if (array[i - 1] > array[i]) { Swap(array, i - 1, i); sorted = false; } } if (sorted == true) ...
【JavaSE】认识String类
创建一个Sting
String有三种构造方法,分别为直接赋值、new String方法构造和字符串数组构造。
1234567//方法1String str1="hello";//方法2String str2=new String("hello");//方法3char[] arr={'h','e','l','l','o'};String str3=new String(arr);
String是一种引用类型,内部并不存储字符串本身。
Sting的存储结构
观察下段代码,输出结果是什么?
1234567String str1="hello";String str11="hello";String str2=new String("hello");String str22=new String("hello");String str3="wor ...
【JavaSE】抽象类和接口
抽象类
什么是抽象类
被abstract修饰的类就是抽象类。抽象类是类的进一步抽象,抽象类中的方法可以不做具体的实现(抽象方法,由abstract修饰)。抽象类中也可以有普通方法。
抽象方法不能由static和final修饰,因为抽象方法要被子类重写。
抽象类中不一定要有抽象方法,但是有抽象方法的类一定是抽象类。
抽象类不能实例化,但可以引用其子类对象。
123456789101112131415161718192021222324252627282930abstract class Animal { public String name; //抽象类中也可以有构造方法,也可以有普通方法、属性 public Animal(String name) { this.name = name; } //抽象方法,不做具体实现 public abstract void eat();}class Dog extends Animal { public Dog(String name) ...