Java多线程基础

等待和唤醒

wait() 线程等待,等待的过程中,释放锁,需要其他线程调用notify去唤醒

notify()唤醒一个等待的线程,如果有多个线程等待,则随机一条唤醒

notifyAll() 唤醒所有等待的线程

Lock锁

是一个接口

使用

获取 其 实现类 ReentrantLock

方法

lock()获取锁

unlock()释放锁

synchronized 与lock的区别

synchronized:不管是同步代码块还是同步方法,都需要在结束一对{}之后,释放锁对象

Lock:是通过两个方法控制需要被同步的代码,更灵活(两个方法为lock和unlock)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class MyTicket implements Runnable {
int ticket = 100;
Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
Thread.sleep(100L);
lock.lock();
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + \"买了第\" + ticket + \"张票\");
ticket--;
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
lock.unlock();
}
}
}
}

public class Test01 {
public static void main(String[] args) {
MyTicket myTicket = new MyTicket();

Thread t1 = new Thread(myTicket, "张三");
Thread t2 = new Thread(myTicket, "李四");
Thread t3 = new Thread(myTicket, "王五");

t1.start();
t2.start();
t3.start();

}

}

Callable

接口,实现多线程的一种方式

Callable是一个接口,类似于Runnable

方法

1.V call() 设置线程任务,类似于run()方法,与之不同的是call可以throw异常,并且还有返回值

2.call方法和run方法的区别:

a.相同点:都是设置线程任务的

b.不同点:

(1)call方法有返回值,而且有异常可以throws

(2)run方法没有返回值,而且有异常不可以throws

3.内容

a.是一个泛型接口。

b.泛型:用于指定我们操作什么类型的数据,<>中只能写引用数据类型,如果泛型不写,默认是Object类型数据。

c.实现Callable接口时,指定泛型是什么类型的,重写的call方法返回值就是什么类型的。

4.获取call方法返回值: FutureTask<V>

a. FutureTask<V> 实现了一个接口: Future <V>

b. FutureTask<V>中有一个方法:

5.V get() -> 获取call方法的返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "ssssss";
}
}
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable myCallable = new MyCallable();
FutureTask<String> futureTask = new FutureTask<>(myCallable);
Thread t1 = new Thread(futureTask);
t1.start();
System.out.println(futureTask.get());
}
}

线程池

容器中有多条线程对象,来了线程任务,直接线程池中获取线程对象,用完还回去。

获取
1
static ExecutorService newFixedThreadPool(int nThreads)
执行线程任务

Futhre<?> submit(Runnable task) 提交一个Runnable任务用于执行

Future<T> submit(Callable<T> task) 提交一个Callable任务用于执行

返回值接口Futher

用于接收run方法或者call方法返回值的,但是run方法没有返回值,所以可以不用Future接收,执行call方法需要用Future接收

Future中有一个方法:V get() 用于获取call方法返回值

关闭

ExecutorService中的方法:

\nvoid shutdown() 启动有序关闭,其中先前提交的任务将被执行,但不会接受任何新任务

练习

需求:创建两个线程任务,一个线程任务完成1-100的和,一个线程任务返回一个字符串

1
2
3
4
5
6
public class MyString implements Callable<String> {
@Override
public String call() throws Exception {
return Thread.currentThread().getName();
}
}
1
2
3
4
5
6
7
8
9
10
public class MySum implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum+=i;
}
return sum;
}
}
1
2
3
4
5
6
7
8
9
10
public class Test01 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建线程池对象
ExecutorService es = Executors.newFixedThreadPool(2);
Future<String> f1 = es.submit(new MyString());
Future<Integer> f2 = es.submit(new MySum());
System.out.println(f1.get());
System.out.println(f2.get());
}
}

Java多线程基础

等待和唤醒

wait() 线程等待,等待的过程中,释放锁,需要其他线程调用notify去唤醒

notify()唤醒一个等待的线程,如果有多个线程等待,则随机一条唤醒

notifyAll() 唤醒所有等待的线程

Lock锁

是一个接口

使用

获取 其 实现类 ReentrantLock

方法

lock()获取锁

unlock()释放锁

synchronized 与lock的区别

synchronized:不管是同步代码块还是同步方法,都需要在结束一对{}之后,释放锁对象

Lock:是通过两个方法控制需要被同步的代码,更灵活(两个方法为lock和unlock)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class MyTicket implements Runnable {
int ticket = 100;
Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
Thread.sleep(100L);
lock.lock();
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + \"买了第\" + ticket + \"张票\");
ticket--;
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
lock.unlock();
}
}
}
}

public class Test01 {
public static void main(String[] args) {
MyTicket myTicket = new MyTicket();

Thread t1 = new Thread(myTicket, "张三");
Thread t2 = new Thread(myTicket, "李四");
Thread t3 = new Thread(myTicket, "王五");

t1.start();
t2.start();
t3.start();

}

}

Callable

接口,实现多线程的一种方式

Callable是一个接口,类似于Runnable

方法

1.V call() 设置线程任务,类似于run()方法,与之不同的是call可以throw异常,并且还有返回值

2.call方法和run方法的区别:

a.相同点:都是设置线程任务的

b.不同点:

(1)call方法有返回值,而且有异常可以throws

(2)run方法没有返回值,而且有异常不可以throws

3.内容

a.是一个泛型接口。

b.泛型:用于指定我们操作什么类型的数据,<>中只能写引用数据类型,如果泛型不写,默认是Object类型数据。

c.实现Callable接口时,指定泛型是什么类型的,重写的call方法返回值就是什么类型的。

4.获取call方法返回值: FutureTask<V>

a. FutureTask<V> 实现了一个接口: Future <V>

b. FutureTask<V>中有一个方法:

5.V get() -> 获取call方法的返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "ssssss";
}
}
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable myCallable = new MyCallable();
FutureTask<String> futureTask = new FutureTask<>(myCallable);
Thread t1 = new Thread(futureTask);
t1.start();
System.out.println(futureTask.get());
}
}

线程池

容器中有多条线程对象,来了线程任务,直接线程池中获取线程对象,用完还回去。

获取
1
static ExecutorService newFixedThreadPool(int nThreads)
执行线程任务

Futhre<?> submit(Runnable task) 提交一个Runnable任务用于执行

Future<T> submit(Callable<T> task) 提交一个Callable任务用于执行

返回值接口Futher

用于接收run方法或者call方法返回值的,但是run方法没有返回值,所以可以不用Future接收,执行call方法需要用Future接收

Future中有一个方法:V get() 用于获取call方法返回值

关闭

ExecutorService中的方法:

\nvoid shutdown() 启动有序关闭,其中先前提交的任务将被执行,但不会接受任何新任务

练习

需求:创建两个线程任务,一个线程任务完成1-100的和,一个线程任务返回一个字符串

1
2
3
4
5
6
public class MyString implements Callable<String> {
@Override
public String call() throws Exception {
return Thread.currentThread().getName();
}
}
1
2
3
4
5
6
7
8
9
10
public class MySum implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum+=i;
}
return sum;
}
}
1
2
3
4
5
6
7
8
9
10
public class Test01 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建线程池对象
ExecutorService es = Executors.newFixedThreadPool(2);
Future<String> f1 = es.submit(new MyString());
Future<Integer> f2 = es.submit(new MySum());
System.out.println(f1.get());
System.out.println(f2.get());
}
}

mysql笔记

MySQL

游标的使用

概述

MySQL游标(cursor)是一种数据结构,用于在存储过程中对结果集中的每一行数据进行独立操作。它可以使开发者对数据库查询结果进行逐行处理,提供了更灵活的控制方式,使得在存储过程中实现复杂的逻辑成为可能。

使用步骤

  1. 声明游标:在存储过程或函数内部,使用 DECLARE 关键字声明游标,格式为 DECLARE 游标名 CURSOR FOR 查询语句;[1][2][3][4][5]。

  2. 打开游标:使用 OPEN 关键字打开游标,使游标指向的结果集准备好供进一步处理[1][2][3][4][5]。

  3. 读取数据:通过 FETCH 语句从游标中读取数据,可以将数据存入变量中,以便进一步处理。FETCH 游标名 INTO 变量名; 可用于提取单一数据,若有多列则需提供多个变量名[1][2][3][4][5]。

  4. 关闭游标:数据处理完成后,使用 CLOSE 关键字关闭游标,释放系统资源[1][2][3][4][5]。

特点

  • 游标是只读的,不能用于修改数据。
  • 游标是不滚动的,只能逐行向前移动,不能跳跃。
  • 游标对于大数据量的处理性能较低,因为它逐行读取数据。

应用场景

游标常用于存储过程的编写,其中涉及到对数据的复杂处理,如条件判断、数据汇总、数据转换等。

示例

假设有一个员工表 employees,包含员工信息,要求写一个存储过程来打印出所有员工的姓名和薪水。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
DELIMITER //
CREATE PROCEDURE PrintEmployeeInfo()
BEGIN
DECLARE finished INT DEFAULT 0;
DECLARE emp_name VARCHAR(100);
DECLARE emp_salary DECIMAL(10, 2);

-- 声明游标
DECLARE cur CURSOR FOR SELECT name, salary FROM employees;

-- 打开游标
OPEN cur;

-- 读取数据
read_loop: LOOP
FETCH cur INTO emp_name, emp_salary;
IF finished THEN
LEAVE read_loop;
END IF;
SELECT CONCAT('Employee Name: ', emp_name, ', Salary: ', emp_salary);
END LOOP;

-- 关闭游标
CLOSE cur;
END //
DELIMITER;

注意事项

  • 确保在使用游标前正确声明并打开它。
  • 在使用完游标后,要及时关闭它,避免资源泄漏。
  • 游标的使用可能会导致性能问题,特别是在大数据量的情况下。

综合分析

游标在MySQL中提供了对结果集逐行处理的能力,使得在存储过程中实现复杂的逻辑变得容易。然而,由于它是逐行读取数据的,所以在处理大量数据时可能会导致性能下降。在使用游标时,需要注意它的特点和适用场景,合理地设计存储过程,以保证系统的效率和稳定性。

触发器概述

MySQL触发器是一种特殊的数据库对象,它能在特定的事件(如INSERT、UPDATE、DELETE)发生时执行预先定义好的SQL语句。触发器可以看作是与表相关联的存储过程,其执行时机完全依赖于特定的数据库事件。

创建触发器

创建触发器的基本语法如下:

1
2
3
4
5
CREATE TRIGGER trigger_name
{BEFORE | AFTER | INSTEAD OF} triggering_event
ON table_name
[FOR EACH ROW] | [FOR EACH STATEMENT]
trigger_body;
  • trigger_name:触发器的名称。
  • BEFORE | AFTER | INSTEAD OF:定义触发器的类型。
  • triggering_event:定义触发器响应的具体事件。
  • table_name:指定触发器关联的表。
  • FOR EACH ROW:表示触发器对每一行数据变动都执行,FOR EACH STATEMENT表示触发器只在每条SQL语句执行后执行。
  • trigger_body:定义触发器内部的SQL语句。

触发器的应用场景

  1. 数据完整性:通过在INSERT、UPDATE、DELETE等操作前后执行触发器,可以确保数据的完整性和一致性。
  2. 日志记录:可以在数据发生变化时记录相关信息,便于追踪和审计。
  3. 业务逻辑处理:可以在触发器中加入复杂的业务逻辑,使得数据库操作更加智能化。

触发器的类型

  • BEFORE INSERT:在INSERT语句执行前触发。
  • AFTER INSERT:在INSERT语句执行后触发。
  • BEFORE UPDATE:在UPDATE语句执行前触发。
  • AFTER UPDATE:在UPDATE语句执行后触发。
  • BEFORE DELETE:在DELETE语句执行前触发。
  • AFTER DELETE:在DELETE语句执行后触发。

删除触发器

删除触发器的基本语法如下:

1
DROP TRIGGER trigger_name;

查看触发器

可以通过SHOW TRIGGERS;命令查看数据库中的所有触发器。

MySQL触发器详细解析

触发器的工作机制

当数据库中定义了触发器后,每当有对应的事件发生时,MySQL就会自动执行触发器中定义的SQL语句。例如,当有数据插入到特定表中时,如果在触发器中定义了AFTER INSERT,那么这些SQL语句就会在INSERT语句执行后立即执行。

触发器中常用的特殊变量

  • OLD:在UPDATE或DELETE触发器中,可以使用OLD来引用原始数据。
  • NEW:在INSERT触发器中,可以使用NEW来引用新插入的数据。

触发器的使用示例

示例1:AFTER INSERT触发器

1
2
3
4
5
6
7
CREATE TRIGGER after_insert_example
AFTER INSERT
ON example_table
FOR EACH ROW
BEGIN
INSERT INTO audit_log (event, data) VALUES ('New record inserted', JSON_OBJECT('key', 'value'));
END;

这个触发器会在每次向example_table表插入数据后,将事件记录到audit_log表中。

示例2:BEFORE DELETE触发器

1
2
3
4
5
6
7
8
9
10
CREATE TRIGGER before_delete_example
BEFORE DELETE
ON example_table
FOR EACH ROW
BEGIN
IF OLD.data_column = 'Important data' THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Cannot delete important data';
END IF;
END;

这个触发器会在每次删除example_table表中的数据前检查data_column列是否含有特定值,如果含有,则阻止删除操作。

MySQL触发器使用方法

使用触发器进行数据校验

可以在触发器中添加数据校验逻辑,确保只有符合业务规则的数据才能被插入或更新。

使用触发器记录操作日志

可以将每次数据库操作的结果记录到日志表中,这对于监控数据库操作和生成报表非常有用。

使用触发器自动化业务流程

可以设计触发器来简化重复性的任务,比如自动更新统计信息、发送通知邮件等。

MySQL触发器综合应用

结合其他数据库对象使用触发器

可以将触发器与存储过程、视图等数据库对象结合使用,构建更为复杂的数据处理逻辑。

使用触发器维护关联数据

当表之间存在关联关系时,可以使用触发器确保相关的数据在插入、更新或删除时同步更新。

注意事项

  • 触发器虽然强大,但也会带来额外的开销,应合理设计避免不必要的性能损耗。
  • 触发器中的SQL语句可能会被多次执行,要注意循环和递归的情况。
  • 某些情况下,触发器可能与事务冲突,需要妥善处理。

结语

MySQL触发器是一个强大的数据库特性,它让我们能够在数据库层面实现很多原本需要在应用程序中处理的逻辑。正确使用触发器,可以增强数据的控制力,提高数据的完整性,简化应用程序的开发。在使用过程中,也需要注意其潜在的副作用,如对性能的影响,以及可能引入的复杂性。

huang2024-5-6-2024-5-12周报

本周学习大纲

1.mysql学习

2.Java整理

3.题目

4.日周笔记

本周学习日历

时间 内容 其他
周一 游标 \
周二 题目 \
周三 Java笔记整理 \
周四 触发器 \
周五 公用表表达式 \
周六 \ 日周笔记
周日 Java笔记整理 \

学习内容

1.mysql笔记

https://snail0220.github.io/2024/05/12/mysql%E7%AC%94%E8%AE%B0-0/

2.Java整理

https://snail0220.github.io/2024/05/09/Java2/

3.题目【web】

image-20240512170925016

4.日周笔记

image-20240512170820030

总结

本周的学习一如既往地缓慢且进行着,本周除了Java笔记的整理和MySQL的学,还进行了C语言第五单元的刷题。总体而言,本周的学习较为充足,对Java基础的认识也越来越完善,学完全部,再进行笔记整理,确实可以获得更多的知识。

下周学习大纲

1.mysql学习

2.Java整理

3.题目

4.日周笔记

2.2.1 if条件语句

if条件语句是一个重要的编程语句,用于告诉程序在某个条件成立的情况下执行某段语句,而在另一种情况下执行另外的语句。

使用if条件语句,可选择是否要执行紧跟在条·件之后的那个语句。关键字if之后是作为条件的”布尔表达式”。如果该表达式返回的结果为true,则执行其后的语句;如果为false,则不执行if条件之后的语句。if条件语句可分为简单的if条件语句、if…else语句和if…else if多分支语句。

1.简单的if条件语句

语法如下

1
2
3
if(布尔表达式){
语句序列
}

*布尔表达式:必要参数,表示最后返回的结果必须是一个布尔值。它可以是一个单纯的布尔变量或常量,也可以是使用关系或布尔运算符的表达式

*语句序列:可选参数。可以是一条或多条语句,当表达式的值为true时执行这些语句。若语句序列中仅有一条语句,则可以省略条件语句中的“{}”。

如果语句序列中只有一条语句,可以采用以下写法:

1
2
3
int a = 100;
if(a == 100)
System.out.println("a的值是100");

过程如下:

image-20240509211626560

【例2.1】判断手机号码是否存在

创建TP类,模拟拨打电话场景,如果电话号码不是84972266,则提示拨打号码不存在。

1
2
3
4
5
6
7
8
public class TP{
public static void main(String[] args){
int Number = 123456789;
if(Number != 84972266){
System.out.prinyln("对不起,您拨打的号码不存在!");
}
}
}

2.if···else语句

if···else语句是条件语句中最常用的一种形式,它会针对某种条件有选择地做出处理。通常表现为“如果满足某种条件,就进行某种处理,否则就进行另一种处理”。语法如下:

1
2
3
4
5
if(条件表达式){
语法序列1
}else{
语句序列2
}

if后面”( )”内的表达式的值必须是boolean型的。如果表达式的值为true,则执行紧跟if语句的复合语句;如果表达式的值为false,则执行else后面的复合语句。过程如下

image-20240509211652957

【例2.2】使用if···else语句校验密码

在项目中创建Login类,在主方法中定义变量,并通过使用if···else语句判断变量的值来决定输出结果。

1
2
3
4
5
6
7
8
9
10
public class Login{
public static void main(String[] args){
int password = 987654321;
if(123456789 == password){
System.out.println("密码正确,欢迎登录");
}else{
System.out.println("密码错误,拒绝登录");
}
}
}

3.if…else if多分支语句

if…else if 多分支语句用于针对某一事件的多种情况进行处理。通常表现为“如果满足某种条件,就进行某种处理,否则如果满足另一种条件则执行另一种处理”。语法如下

1
2
3
4
5
6
7
8
9
if(条件表达式1){
语句序列1
}else if(条件表达式2){
语句序列2
}
...
else if(条件表达式n){
语句序列n
}

执行过程如图所示

image-20240512161207307

【例2.3】使用if…else if语句实现饭店座位分配

创建rest类,声明整型变量count表示用餐人数,根据人数安排客人到4人桌,8人桌或包厢用餐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class rest{
public static void main(String args[]){
System.out.println("欢迎光临,请问有多少人用餐?");
int count = 9
System.out.println("回答:"+count+"人");
if(count<=4){
System.out.println("客人请到大厅4人桌用餐");
}else if(count>4&&count<=16){
System.out.println("客人请到大厅8人桌用餐");
}else if(count>8&&count<=16){
System.out.println("客人请到楼上包厢用餐");
}else{
System.out.println("抱歉,我们店暂时没有这么大的包厢!");
}
}
}

2.2.2 Switch多分支语句

在编程中,一个常见的问题就是检测一个变量是否符合某个条件。如果不符合,再用另外一个值来检测,以此类推。当然用if条件语句也可以完成。

例如,使用if语句对考试成绩进行评估,关键代码如下:

1
2
3
4
5
6
if(grade == 'A'){
System.out.println("真棒");
}
if(grade == 'B'){
System.out.println("做的不错");
}

在Java中,可以用Switch语句将动作组织起来,以一个比较简单明了的方式来实现“多选一”的选择。语法如下:

1
2
3
4
5
6
7
8
9
10
11
12
switch(表达式){
case 常量值1
语句块1
[break;]
...
case 常量值n:
语句块n
[break;]
default:
语句块n+1;
[break;]
}

switch语句中表达式的值必须是整型、字符型、字符串类型或枚举类型,常量值1~n的数据类型必须与表达式的值的类型相同。

switch语句首先计算表达式的值,如果表达式的计算结果和某个case后面的常量值相同,则执行该case语句后的若干个语句直到遇到break语句为止。此时,如果该case语句中没有break语句,将继续执行后面case中的若干个语句,直到遇到break语句为止。若没有一个常量的值与表达式的值相同,则执行default后面的语句。default语句为可选的,如果它不存在,且switch语句中表达式的值不与任何case的常量值相同,switch语句则不做任何处理。

image-20240512163659523

注意
(1)同一个switch语句,case的常量值必须互不相同。
(2)在switch语句中,case语句后常量表达式的值可以为整数,但绝不可以是非整数的实数。例如,下面的代码就是错误的:

【例2.4】使用switch语句分为考试分数分级

创建Grade类,使用Scanner类在控制台输入分数,然后用switch多分支语句判断输入的分数属于哪类成绩。10分和9分属于优,8分属于良,7分和6分属于中,5分、4分、3分、2分、1分以及0分均属于差。

image-20240512165633843

Java多线程基础

等待和唤醒

wait() 线程等待,等待的过程中,释放锁,需要其他线程调用notify去唤醒

notify()唤醒一个等待的线程,如果有多个线程等待,则随机一条唤醒

notifyAll() 唤醒所有等待的线程

Lock锁

是一个接口

使用

获取 其 实现类 ReentrantLock

方法

lock()获取锁

unlock()释放锁

synchronized 与lock的区别

synchronized:不管是同步代码块还是同步方法,都需要在结束一对{}之后,释放锁对象

Lock:是通过两个方法控制需要被同步的代码,更灵活(两个方法为lock和unlock)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class MyTicket implements Runnable {
int ticket = 100;
Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
Thread.sleep(100L);
lock.lock();
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + \"买了第\" + ticket + \"张票\");
ticket--;
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
lock.unlock();
}
}
}
}

public class Test01 {
public static void main(String[] args) {
MyTicket myTicket = new MyTicket();

Thread t1 = new Thread(myTicket, "张三");
Thread t2 = new Thread(myTicket, "李四");
Thread t3 = new Thread(myTicket, "王五");

t1.start();
t2.start();
t3.start();

}

}

Callable

接口,实现多线程的一种方式

Callable是一个接口,类似于Runnable

方法

1.V call() 设置线程任务,类似于run()方法,与之不同的是call可以throw异常,并且还有返回值

2.call方法和run方法的区别:

a.相同点:都是设置线程任务的

b.不同点:

(1)call方法有返回值,而且有异常可以throws

(2)run方法没有返回值,而且有异常不可以throws

3.内容

a.是一个泛型接口。

b.泛型:用于指定我们操作什么类型的数据,<>中只能写引用数据类型,如果泛型不写,默认是Object类型数据。

c.实现Callable接口时,指定泛型是什么类型的,重写的call方法返回值就是什么类型的。

4.获取call方法返回值: FutureTask<V>

a. FutureTask<V> 实现了一个接口: Future <V>

b. FutureTask<V>中有一个方法:

5.V get() -> 获取call方法的返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "ssssss";
}
}
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable myCallable = new MyCallable();
FutureTask<String> futureTask = new FutureTask<>(myCallable);
Thread t1 = new Thread(futureTask);
t1.start();
System.out.println(futureTask.get());
}
}

线程池

容器中有多条线程对象,来了线程任务,直接线程池中获取线程对象,用完还回去。

获取
1
static ExecutorService newFixedThreadPool(int nThreads)
执行线程任务

Futhre<?> submit(Runnable task) 提交一个Runnable任务用于执行

Future<T> submit(Callable<T> task) 提交一个Callable任务用于执行

返回值接口Futher

用于接收run方法或者call方法返回值的,但是run方法没有返回值,所以可以不用Future接收,执行call方法需要用Future接收

Future中有一个方法:V get() 用于获取call方法返回值

关闭

ExecutorService中的方法:

\nvoid shutdown() 启动有序关闭,其中先前提交的任务将被执行,但不会接受任何新任务

练习

需求:创建两个线程任务,一个线程任务完成1-100的和,一个线程任务返回一个字符串

1
2
3
4
5
6
public class MyString implements Callable<String> {
@Override
public String call() throws Exception {
return Thread.currentThread().getName();
}
}
1
2
3
4
5
6
7
8
9
10
public class MySum implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum+=i;
}
return sum;
}
}
1
2
3
4
5
6
7
8
9
10
public class Test01 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建线程池对象
ExecutorService es = Executors.newFixedThreadPool(2);
Future<String> f1 = es.submit(new MyString());
Future<Integer> f2 = es.submit(new MySum());
System.out.println(f1.get());
System.out.println(f2.get());
}
}

mysql锁

MySQL全局锁的定义和作用

定义

MySQL全局锁是指对整个数据库实例加锁,即数据库中的所有表都将被加上锁。当全局锁被应用时,整个数据库实例会处于只读状态,后续的DML(数据操纵语言)的写语句,如数据的增删改操作,DDL(数据定义语言)语句,如建表、修改表结构等操作,以及更新操作的事务提交语句都将被阻塞。

作用

全局锁的主要作用是确保数据库备份的完整性和一致性。它防止了在备份过程中对数据库进行修改,从而保证了备份数据的完整性。此外,在主从复制架构下,全局锁还可能用于主备切换的场景中,以确保数据的一致性传递。

MySQL全局锁的使用场景

数据备份

在进行数据库逻辑备份时,为了避免备份过程中数据被修改,通常会对数据库加全局锁。这样可以确保备份出来的数据是一致的,没有因为并发操作导致的差异。

主从复制与切换

在主从复制架构中,当需要进行主备切换时,也可能需要使用全局锁。这是因为在切换过程中,需要确保主库和备库的数据一致性,防止数据丢失或不一致。

MySQL全局锁的操作步骤

加锁

加锁通常通过执行FLUSH TABLES WITH READ LOCK;(FTWRL)命令来实现。该命令会对数据库中的所有表加读锁,不允许任何写操作,但允许读取操作。这意味着,直到解锁前,数据库将处于只读状态。

解锁

备份完成后,可以使用UNLOCK TABLES;命令来释放全局锁,这将使得数据库恢复到正常的读写状态。

MySQL全局锁的特点

特点概述

全局锁是一种重量级的锁定机制,它对整个数据库实例生效。这种锁能有效阻止数据在备份过程中的更改,但也意味着在备份期间,数据库几乎不能进行任何更新操作,这可能会对业务造成影响。

详细特点

  • 当对主库使用全局锁时,所有更新操作都会被阻塞,因此,业务基本上会暂停。
  • 如果选择在从库上使用全局锁进行备份,那么从库在备份期间无法同步主库的二进制日志(binlog),这可能会导致主从延迟。
  • 使用全局锁时,必须非常小心,因为它会严重影响数据库的性能,并可能导致并发性能的显著下降。

总结

综上所述,MySQL全局锁是一种重要的数据库锁定机制,它在数据库备份和主从复制等方面起到了确保数据一致性的作用。然而,由于它会锁定整个数据库实例,因此在非必要时应尽量避免使用,以避免对业务造成不必要的干扰。在使用全局锁时,需要仔细评估其对数据库性能的影响,并采取适当的解锁措施,以保障数据库的正常运行。

MySQL表级锁概述

定义和作用

MySQL中的表级锁是一种锁定机制,用于控制对数据库表的访问,保证数据的安全性和完整性。它允许用户对特定的表进行锁定,从而防止其他用户对其进行修改,确保数据在读取或修改时的稳定性。

使用场景

表级锁适用于多种场景,例如在进行大数据量的插入、删除或更新操作时,为了防止数据丢失或数据不一致,需要对这些操作进行锁定。此外,当多个用户或进程需要对同一数据进行访问时,也需要用到表级锁以确保数据的一致性。

操作步骤

使用表级锁的基本步骤包括:

  1. 确定需要锁定的数据范围,选择合适的锁类型。
  2. 执行锁定命令,如LOCK TABLES
  3. 进行相关的数据操作。
  4. 完成操作后,释放锁,使用UNLOCK TABLES命令。

MySQL表级锁详细解析

表级锁的种类

MySQL中的表级锁主要有以下几种:

  • 表锁:分为读锁和写锁,用来控制对表的读写访问。
  • 元数据锁(MDL):用来控制对表的DDL操作,如创建、修改或删除表结构。
  • 意向锁:用来协调不同类型的锁请求,避免不必要的锁冲突。

表级锁的工作机制

表级锁的工作基于以下几个关键组件:

  • 锁队列:包含当前持有锁的线程信息和等待锁的线程信息。
  • 锁类型:如普通读写锁、元数据锁等。
  • 锁的升级和解锁:根据操作的需要,锁可以从一种类型升级为另一种类型,或者在操作完成后释放。

注意事项

在使用表级锁时,需要注意的事项包括:

  • 不要滥用锁,应只在必要时使用。
  • 要确保锁与操作相对应,比如读操作对应的是读锁,写操作对应的是写锁。
  • 在进行长时间运行的操作时,考虑可能的锁超时问题。

MySQL表级锁实践指南

实际操作示例

假设需要对某个表进行修改,并且在这个过程中不允许其他线程对其进行干扰,可以按照以下步骤操作:

  1. 使用LOCK TABLES命令对该表加写锁。
  2. 进行必要的修改操作。
  3. 使用UNLOCK TABLES命令释放写锁。

监控和调试

在实际工作中,可能需要监控锁的情况,可以使用SHOW OPEN TABLES命令来查看当前打开的锁。如果出现问题,可以通过EXPLAIN PLAN FOR命令来分析锁的情况。

总结

MySQL的表级锁是一个强大的工具,它可以有效地保护数据安全性和完整性。然而,不当使用可能会导致性能问题和锁争用。因此,合理规划和使用表级锁,结合业务需求和数据库特性,是数据库管理员的重要职责。

huang2024-4-29-2024-5-5周报

本周学习大纲

1.日周笔记

2.mysql笔记

3.Java笔记整理

本周学习日历

日期 内容 其他
星期一 全局锁 /
星期二 Java笔记 日周笔记
星期三 / /
星期四 表级锁 /
星期五 行级锁 /
星期六 Java 笔记 /
星期日 / /

本周学习内容及收获

1.mysql笔记

https://snail0220.github.io/2024/05/05/mysql%E9%94%81/

2.Java笔记整理

https://snail0220.github.io/2024/04/08/java/

3.日周笔记

image-20240505215208369

总结

本周的学习情况及状态正常,唯一的例外就是在假期会有些时候没有自控力,耽误了一些学习的安排,浪费之后的时间,打乱原有的计划,希望自己可以提高自控力。

下周学习大纲

1.mysql学习

2.Java整理

3.题目

4.日周笔记

MySQL存储过程

MySQL存储过程

概述

存储过程的定义

存储过程(Stored Procedure)是一种在数据库中封装好的SQL代码片段,它可以包含一个或多个SQL语句,用于完成特定的数据处理任务。存储过程可以接收输入参数,并返回结果,有着和程序语言相似的结构,可以实现复杂的逻辑判断和计算。

存储过程的特点
  1. 能够完成复杂的判断和运算;

  2. 具有较高的编程灵活性;

  3. SQL编程代码可重复使用;

  4. 执行速度较快;

  5. 减少网络数据传输,节省资源。

创建和调用存储过程

创建存储过程使用CREATE PROCEDURE语句,调用存储过程使用CALL语句。例如:

1
CREATE PROCEDURE test_procedure() BEGIN SELECT * FROM users; SELECT * FROM orders; END; CALL test_procedure(); 

以上是一个简单的存储过程示例,其中包含了两个SELECT语句,分别查询users和orders表。

存储过程中的变量

存储过程可以定义和使用变量,使用DECLARE语句声明变量,SET语句赋值。变量可以是局部或全局范围,取决于它们的声明位置。例如:

1
CREATE PROCEDURE test_variable_procedure() BEGIN DECLARE username VARCHAR(32); SET username = 'xiaoxiao'; SELECT name INTO username FROM users WHERE id = 1; SELECT username; END; 

在这个示例中,首先声明了一个VARCHAR类型的变量username,然后将其设置为’xiaoxiao’,接着从数据库中查询id为1的用户姓名并存入该变量,最后返回该变量的值。

存储过程的作用域

存储过程中的变量作用域仅限于BEGIN和END块之间。这意味着一旦存储过程执行完毕,声明的变量就会被销毁,不会再有任何作用。

综合示例

以下是一个包含多个组件的复杂存储过程示例:

1
CREATE PROCEDURE complex_procedure(IN user_id INT, OUT total_count INT) BEGIN DECLARE product_count INT; BEGIN SELECT COUNT(*) INTO product_count FROM products WHERE user_id = user_id; SET total_count = product_count + 1; IF user_id = 1 THEN INSERT INTO purchases (user_id, product_id, quantity) VALUES (user_id, 1, 1); END IF; END; END; CALL complex_procedure(1, @total_count); 

在此示例中,我们定义了一个接受IN参数和OUT参数的存储过程,其中涉及到变量声明、条件判断以及插入操作。@total_count是一个预备好的变量,用于存储过程执行后的总计数量。

结论

综上所述,MySQL存储过程是一种强大的数据库编程工具,它允许我们在数据库服务器上执行预先编写好的SQL脚本,可以有效地处理数据并优化数据库操作。在使用存储过程时,我们可以利用它的特点和优势,为数据库应用提供更高效、更可靠的服务。

CSS美化网页

使用CSS技术美化网页

HTML存在很大的局限和不足。

3.1【案例4】数字变色Logo

1.认识CSS

实现结构与表现的分离。CSS非常灵活,既可以嵌入在HTML文档中,也可以是一个单独的外部文档,如果是独立的文档,则必须以.css为后缀。

CSS集中写在HTML文档的头部,也是符合结构与表现相分离的。

1
2
3
4
5
6
7
<style type="text/css">
p{
font-size:36px;
color:red;
text-align:center
}
</style>

优势:节约成本、提高性能

2.CSS样式规则

具体语法规则如下

1
2
3
4
选择器{
属性1:属性值1
属性2:属性值2
}

特点

  • 严格区分大小写。

  • 多个属性之间必须用英文状态下的分号隔开,最后一个属性后的分号可以省略,但是最好保留。

  • 如果属性值由多个单词组成且中间包含空格,则必须为这个属性值加上英文状态下的引号。

    1
    p {font-family:"Times New Roman";}
  • 可以使用“/注释语句/”来进行注释

    例如,上面样式代码可添加

    1
    2
    p {font-family:"Times New Roman";}
    /* 这是CSS注释文本,有便于查找代码,此文本不会显示在浏览器窗口中 */
  • CSS中空格是不会被解析的,花括号以及分号前后的空格可有可无。因此可以使用空格键、tab键对样式进行排版。

3.引入CSS样式表

要想使用CSS修饰网页,就需要在HTML文档中引入CSS样式表。CSS提供4种引入方式,分别为行内式、内嵌式、外链式、导入式。

(1)行内式

行内式也被称为内联样式,是通过标签的style属性来设置标签的样式,其基本语法格式如下。

1
<标签名 style="属性1:属性值1;属性2:属性值2;属性3:属性值3;">内容</标签名>

行内式也称内联样式,

内嵌式

内嵌式是将CSS代码集中写在HTML文档的head头部标签中,并且用style标签定义,其基本语法如下。

1
2
3
4
5
<head>
<style type="text/css">
选择器 {属性1:属性值1;属性2:属性值2;属性3:属性值3;}
</style>
</head>

外链式

外链式也称链入式,是将所有的样式放在一个或多个以”.css”为扩展名的外部样式表文件中,通过link/标签将外部样式表文件连接到HTML文档中,其基本语法格式如下。

1
2
3
<head>
<link herf= "CSS文件的路径" type="text/css" rel="stylesheet"/>
</head>

link/标签需要放在head头部标签中,并且必须指定link/标签的3个属性,具体如下。

导入式

导入式与外链式相同,都是将样式存放在外部样式表文件中。对HTML头部文档应用style标签,并在style标签内的开头处使用@import语句,即可导入外部样式表文件,其基本语法格式如下。

1
2
3
4
<style type="text/css">
@import url(css文件路径);或@import "css文件路径"
/*在此处还可以存放其他的CSS样式*/
</style>

4.CSS基础选择器

(1)标签选择器

(2)类选择器

(3)id选择器

(4)通配符选择器

3.2【案例5】美食专题栏目

image-20240427164652653

huang2024-4-22-2024-4-28周报

本周学习目标

1.MySQL学习

2.java笔记整理

3.日周笔记

日历安排

时间 内容 其他
星期一 存储过程(基本、变量) \
星期二 Java笔记 \
星期三 CSS技术美化网页 \
星期四 存储过程(if判断、循环) \
星期五 \ 日周笔记
星期六 Java笔记 \
星期日 C语言题目 \

本周学习内容

1.MySQL笔记

https://snail0220.github.io/2024/04/28/MySQL%E5%AD%98%E5%82%A8%E8%BF%87%E7%A8%8B/

2.CSS美化网页笔记

https://snail0220.github.io/2024/04/27/CSS%E7%BE%8E%E5%8C%96%E7%BD%91%E9%A1%B5/

3.日周笔记

image-20240427161413855

4.c语言题目

5.Java笔记

https://snail0220.github.io/2024/04/08/java/

总结

本周的学习一如既往地缓慢且进行着,本周除了Java笔记的整理和MySQL的学,还进行了C语言第六单元的刷题。总体而言,本周的学习较为充足,对Java基础的认识也越来越完善,学完全部,再进行笔记整理,确实可以获得更多的知识。

下周学习安排

1.mysql学习

2.Java笔记整理

3.日周笔记