第四周周报小结:感觉这一周过得是最快的,前两天复习了之前的线程和一些Java高级部分比较难懂得部分,复习了一些我们之前没有很明白的知识,让我们对之前的知识有了更加深刻地印象,然后就暂时结束了Java的学习,感觉之前学的很多都是听过去了,并没有太多的去回顾之前的知识,有一些还不是很懂得地方也没有回去消化,一是没时间,二十懒得去回顾,不过时间还会有的,主要是自己没有主动去想搞学习的意愿,再跟别的同学交流之后,别人感觉都很努力,我感觉还是要更加努力一点,比你优秀的人还比你努力,那我还有什么借口不去努力,以后回家还是每天都要看一会,熬过这四个月就好了,现在只有三个月了,周三周四讲了一些比较简单的数据库查询语句,感觉还是比较好懂的,周五柴佬没在,上午也不知道在干什么,下午又出去玩了,周六一晃就过去了,又放假了,这一周感觉像做梦一样的,过的是真的快,从下周起一定要每天回去都搞会学习,下周再来汇报情况吧,这就是我本周的小结。
本周学习内容:
线程并发
多线程应用中如果涉及到多个线程操作共享变量,极有可能出现线程并发导
致数据不安全问题,例如银行账户取钱问题:
有一个银行账户,还有余额1100
元,现在A通过银行卡从中取1000元,而同时另外一个人B通过存折也从这个账
户中取1000元。取钱之前,要首先进行判断:如果账户中的余额大于要取的金
额,则可以执行取款操作,否则,将拒绝取款。
我们假定有两个线程来
分别从银行卡和存折进行取款操作,当A线程执行完判断语句后,获得了当前账
户中的余额数(1000元),因为余额大于取款金额,所以准备执行取钱操作(
从账户中减去1000元),但此时它被线程B打断,然后,线程B根据余额,从中
取出1000元,然后,将账户里面的余额减去1000元,然后,返回执行线程A的动
作,这个线程将从上次中断的地方开始执行:也就是说,它将不再判断账户中
的余额,而是直接将上次中断之前获得的余额减去1000。此时,经过两次的取
款操作,账户中的余额为100元,从账面上来看,银行支出了1000元,但实际上
,银行支出了2000元。
账户类:Account.java
public class Account {
private String num; //账号
private double cash; //余额
public Account(String num,double cash) {
this.num = num;
this.cash = cash;
}
public String getNum() {
return num;
}
public void setNum(String num) {
this.num = num;
}
public double getCash() {
return cash;
}
public void setCash(double cash) {
this.cash = cash;
}
}
取款线程类:
/**
* 用于完成取款操作的线程类
* @author mrchai
*/
public class AccountManager implements Runnable{
private Account account; //需要被取款的账户
private double money; //需要取走金额
public AccountManager(Account account, double money) {
super();
this.account = account;
this.money = money;
}
@Override
public void run() {
//判断账户中的余额是否足够
if(account.getCash() >= money){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//减少账户的余额
account.setCash(account.getCash() -
money);
System.out.println
(Thread.currentThread()+"取款成功,余额:"+account.getCash());
}else{
System.out.println
(Thread.currentThread()+"取款失败,余额不足!");
}
}
测试类Client.java
public class Client {
public static void main(String[] args) {
//创建账户类,余额10000
Account a = new Account("0001", 10000);
//创建Runnable对象,每次取款10000
AccountManager am = new AccountManager(a, 10000);
Thread t1 = new Thread(am);
Thread t2 = new Thread(am);
//启动两个取钱线程
t1.start();
t2.start();
}
}
结果:Thread[Thread-0,5,main]取款成功,余额:0.0
Thread[Thread-1,5,main]取款成功,余额:0.0
根据结果显示,如果时间点恰到好处(两个线程同时进入查询,发现余额足够)
,两个线程都能成功取钱,这对银行就不公平(损失10000);
Synchronized
以上问题就是线程并发共享变量时引起的数据结果不一致的问题;针对以上
的问题,可以通过同步块或者同步方法来解决,通过synchronized可以实现对指
定对象或者方法锁定,一旦锁定,则当前线程结束对该对象或该方法使用前其
他线程无法进行操作,从而确保数据同步。
-
同步块:使用synchronized语句块锁定指定对象
-
同步方法:在方法返回类型前使用synchronized关键字修饰
死锁
在多线程并发操作时,如果使线程同步,则极有可能造成死锁,死锁主要是
有多个线程在等待对方释放所占有的资源时引起,死锁无法解决只能尽量避免
UDP数据广播
在通用的以太网(Ethernet)构架下,计算机于计算机之间的数据交换都是
通过交换机来完成的。如果一份数据需要被传送给多个接收者,在使用TCP/IP
连接的情况下,数据发送者需要向交换机发送N 个同样的拷贝,而交换机则负
责将这N 个拷 贝分发给所有的接收者;
在使用UDP 数据广播的情况下,数据发送者只需要向交换机发送一个拷贝
,交换机负责将这个信息制作N 个拷贝发送给所有的机器。在这种情况下,使
用TCP/IP连接会大大的增加网络的负担。在一个普通局域网络中,可以认为由
于网络状况较差而造成数据丢失的可能性比较小,而利用UDP 数据广播进行 数
据交换能够大幅度减轻网络的负担 。
UDP数据广播核心类:
- MulticastSocket
- DatagramPacket
广播发送
public class MulticastDemo {
public static void main(String[] args) throws IOException {
String msg = "本周五下午举办第一次班级过活动,望广大
童鞋积极参加,相互转告!!!!";
//创建组播地址对象
InetAddress ip = InetAddress.getByName("228.5.6.7");
//创建数据广播通道对象
MulticastSocket ms = new MulticastSocket();
//将组播地址加入网络通道中
ms.joinGroup(ip);
//将消息内容打包成数据报包
DatagramPacket dp = new DatagramPacket(msg.getBytes
(),0, msg.getBytes().length, ip, 6789);
//发送数据广播
ms.send(dp);
ms.close();
}
}
接收广播
public class MulticastDemo_Receiver {
public static void main(String[] args) throws IOException {
InetAddress ip = InetAddress.getByName("228.5.6.7");
MulticastSocket ms = new MulticastSocket(6789);
//将组播地址加入网络通道中
ms.joinGroup(ip);
byte[] b = new byte[1024];
//将消息内容打包成数据报包
DatagramPacket dp = new DatagramPacket(b,b.length);
ms.receive(dp);
String s = new String(dp.getData(),0,dp.getLength());
System.out.println(s);
}
}
数据库内容:
mysql常用命令
系统命令
(以管理员身份运行)
#启动服务
net start mysql
#停止服务
net stop mysql
#进入mysql命令行
mysql -uroot -p123456
#退出mysql命令行
exit
#修改密码
mysqladmin -uroot -p123456 password root
Mysql常用命令
--显示数据库实例
show databases;
--创建数据库实例
create database mydb;
--使用数据库实例
use mydb;
--显示实例中所有数据库表
show tables;
SQL语句
结构化查询语言,一门关系型数据中通用的操作语言,语法结构类似英语口语
,sql语句通常划分为三类
- DDL
- DML
- DCL
DDL
/*
创建用户表,表中包含用户id,用户名,密码
*/
create table tbuser(
id int,
username varchar(10),
password varchar(20)
);
--查看表结构
desc tbuser;
--向表中新增level列是int类型
alter table tbuser add level int;
--修改表中列的(将level列的名称改变为dengji)
alter table tbuser change level dengji int;
--删除表
drop table tbuser;
DML语句
--向表中插入一条数据
insert into tbuser(username,password) values('softeem','123456');
--批量插入数据
insert into tbuser values(1,'rose','666'),(2,'jack','8888'),
(3,'tom','999');
--蠕虫复制
insert into tbuser select * from tbuser;
--查询表中所有数据
select * from tbuser
--修改所有id为null的列为0
update tbuser set id=0 where id is null;
--修改多列数据
update tbuser set password='admin',username='admin' where id=1;
--删除id为0的所有数据
delete from tbuser where id=0;
约束
约束是维护数据完整型的一种操作,在数据库中维护数据完成性操作中包含以
下解决方案:
- 约束(contraint):最简单最容易实现的一种方式
- 存储过程(procedure)
- 触发器(trigger)
约束主要包含五大类:
1. 主键约束
2. 外键约束
3. 不为空约束
4. 唯一约束
5. 检查约束(mysql暂不支持)
--修改制定列设置列不为空
alter table user change username username varchar(16) not null;
--使用修改语句设置表的列为唯一列
alter table user add constraint uq_name unique(username);
外键约束
通过设置外键约束可以让当前一张表与另一张表产生约束关系,在执行更新操
作是,会通过约束的关系保证数据的完整性(被设置外键的列必须关联另一张表
的主键列)
数据库备份
备份
(系统命令行)
#备份数据库实例
mysqldump -uroot -proot mydb > d:/mydb.sql
#备份表
mysqldump -uroot -proot mydb tbuser > d:/tbuser.sql
auto_increment:设置列自增,可用于主键列以及非空唯一列(not null
unique)
unsigned:设置无符号列(列值不允许为负数)
zerofill:设置零填充列(当列数据长度不到定义长度时,数值前补0)
char类型跟varchar类型区别
char类型是定长字符串,默认大小是255(最大255);char类型一旦定义长度
,则无论实际添加的数据长度是否达到最大长度,都会将定义空间占满,char
类型效率高一般用于长度固定的列。
varchar类型是定长字符串,默认大小是255(最大长度取决与数据表的编码模
式,utf-8:65535/3;GBK:65535/2),varchar类型定义长度之后系统不会直
接分配对应大小的空间,而是根据实际填充值的长度申请空间,相对char类型
更节省空间,所以varchar用于长度不定的列
SQL查询
sql语句DML语句中最为复杂的是查询语句,insert,update,delete语句多数情
况为单表操作,但是select与可以关联多张数据表,针对sql查询需要明确三点
:
1. 查询列
2. 查询目标表
3. 查询条件
SQL查询语法:
SELECT 查询列1,查询列2,... FROM 目标表
【WHERE 查询条件】
【GROUP BY 列名称】
【HAVING 查询条件】
【ORDER BY 列名称 ASC|DESC】
【LIMIT [偏移行,]记录行数】
sql查询常见关键字:
- from子句:指定查询数据的表
- where子句:查询数据的过滤条件
- group by子句:对匹配where子句的查询结果进行分组
- having子句:对分组后的结果进行条件限制
order by子句:对查询结果结果进行排序,后面跟desc降序或asc升序(默认
)
- limit子句:对查询的显示结果限制数目
单表查询
--查询所有员工的姓名,职位,月薪
select ename,job,sal from emp;
--查询月薪超过10000的员工
select * from emp where sal>10000;
--查询入职时间在2017-01-01~2017-12-31之间的员工
select * from emp where hiredate between '2017-01-01' and '2017-12-
31';
--查询所有市场经理和项目经理信息
select * from emp where job in('市场经理','项目经理');
--模糊查询 ‘%’ ‘_’
--查询员工姓名中包含‘杰’的员工
select * from emp where ename like '%杰%';
--查询员工中所有的‘张’姓员工
select * from emp where ename like '张%';
--查询员工中‘周’姓名字只有两个字的员工
select * from emp where ename like '周_';
--聚合函数:count() max() min() sum() avg()
--查询出emp表中的总记录行数
select count(*) from emp;
--查询出emp表中的最高/低薪资 max/min
select max(sal) from emp;
--统计一个月需要支出的共工资?
select sum(sal) from emp;
--统计1号部门的平均薪资?
select avg(sal) from emp where dno=1;
--查询出所有的职位名称去除重复(distinct)
select distinct job from emp;
--分组 group by...having...
--查询出每种岗位的平均薪资?
select avg(sal),job from emp group by job;
--查询出每种岗位平均薪水大于8000的岗位名和平均薪水
select avg(sal),job from emp group by job having acg(sal)>8000;
--排序 order by ... desc|asc
--查询出员工信息按照月薪从高到低排序
select * from emp order by sal desc;
--限定查询结果limit [offset,]rowcount
--查询月薪前三的员工
select * from emp order by sal desc limit 3;
--查询月薪排名第4~6的员工
select * from emp order by sal desc limit 3,3;
多表联合查询
--连接查询(等值连接)
--查询出所有员工信息和部门信息
select e.*,d.* from emp e,dept d where e.dno=d.dno;
--内连接:从左表中取出每一条记录,去右表中与所有的记录进行匹配: 匹配必
须是某个条件在左表中与右表中相同-最终才会保留结果,否则不保留.
select e.*,d.* from emp e inner join dept d on e.dno=d.dno;
--通过对以上结果观察会发现没有员工的部门,以及没有部门的员工都不会显
示,如何解决?
--左连接(左外连接):以左表为基准连接右表,左表信息完全显示
select e.*,d.* from emp e left join dept d on e.dno=d.dno;
--右连接(右外连接):以右表为基准连接左表,右表信息完全显示
select e.*,d.* from emp e right join dept d on e.dno=d.dno;
子查询
子查询允许把一个查询嵌套在另一个查询当中。子查询,又叫内部查询,相对
于内部查询,包含内部查询的就称为外部查询。子查询可以包含普通select可
以包括的任何子句,比如:distinct、 group by、order by、limit、join和
union等;但是对应的外部查询必须是以下语句之一:select、insert、update
、delete、set或 者do。
子查询分为如下几类:
1. 标量子查询:返回单一值的标量,最简单的形式。
2. 列子查询:返回的结果集是 N 行一列。
3. 行子查询:返回的结果集是一行 N 列。
4. 表子查询:返回的结果集是 N 行 N 列。