📄 thread.txt
字号:
代码实现:
1、服务端:必须公开一个端口。
//产生服务器的实例
ServerSocket ss=new ServerSocket(4001);
//监听并到到一个会话
Socket socket=ss.accept();
//得到输入的信息
socket.getInputStream();
//向外输出信息
socket.getOutputStream();
2、客户端:通过ip+端口连接服务端
//连接服务端
Socket socket=new Socket("主机名/ip",4001);
//得到输入的信息
socket.getInputStream();
//向外输出信息
socket.getOutputStream();
2、基于udp协议,
特点:不产生会话,可以广播,可以批量发送。
DatagramSocket:发送接收信息,在发送时在DatagramSocket
不但要封装字节数据,还要封要封装对应的ip地址及
端口。
在接口时,只需封装字节数据,取值就可以了。
send(datagrampacket);
receive(datagramPacket);
DatagramPacket:承载信息
二、多线程服务框架:在服务端部署一个返转的服务,多个客户端向其发送信息后,
其可以将该信息返转,再返回给客户端。
1234--->4321
应当有几类线程:
1、监听线程
2、会话线程
有一个普通类
1、服务类
系统先启动的是监听线程,监听线程接收到一个请求后产生一个会话线程,
会话线程调用服务类做具体的处理。
连接服务:
telnet 主机名 端口
五、wait、notify、notifyAll
1、三个关键必须在同步块中或同步语句中执行,否则报错。
2、wait是使当前线程处于等待状态并释放其所占用资源,其下的代码堆栈,
当被激活后,执行wait语句的下一句,相当于代码的暂停,而不是停止。
3、notify从等待队列中激活一个线程。
4、notifyAll激活整个等待队列中的所有线程
七、仓库中生产产品与消费立品的处理
Thread.currentThread:得到调用当前方法的线程
1、一个仓库,用队列来实现
入库:生产者线程生产产品放到仓库中,具体的要求如下:
1、只有生产者线程才可以入库
boolean isRunning=false;
Thread thread=Thread.currentThread();
if(thread instanceof Producer)
{
}
else
{
return;
}
isRunning=((Producer)thread).getRunning();
2、当仓库已满且生产者线程在运行时,让生产者线程等待。必须
用循环,不能用if,因生产者被激活后可以仓库还是满的。
while(isRunning && store.isFull)
{
wait();
}
3、当激活还要判断当生产者是否在运行,有可能外界已不让其运行了。
isRunning=((Producer)thread).getRunning();
if(!isRunning)
{
return;
}
4、生产者生产一个产品增加仓库中。
2、多个生品者线程
3、多个消费者线程
九、join:让主线程等待所有的子线程执行结束后再结束
如果不join,主线程结束了,子线程还在运行。
线程实例.join();
十二、 setPriority:(1--10),一般情况下优先级高的先执行。
默认是5,最大是10
十、daemon:守护线程,JVM就是一个守护线程,守护线程主要其它线程提供服务。
在方法之前设置
为非守护线程提供服务,当所有非守护线程结束后,不管其是否运行完毕都会自动结束。
由一个守护线程产生的新线程也是守护线程
十一、ThreadGroup:线程组
任何一个线程都在一个线程组中,线程组下可以有子线程组及线程。
getParentGroup:得到父线程组
currentThread:得到当前线程
getThreadGroup:得到当前线程所在的线程组
new Thread(所属线程组,Runnable,name)
new ThreadGroup(所属线程组,线程组名)
遍历线程组下的所有线程
一、取出虚拟机下的所有线程
线程组:ThreadGroup, 一个线程组可以有多个线程,也可以有很多子线程组
线程:Thread
实例化线程:
new Thread(Runnable run);
new Thread(Runnable run,String ThreadName);
new Thread(String ThreadName);
//基于runnable实现一个线程,ThreadName是线程的名称,ThreadGroup代表当前线程属于那一个线程组
new Thread(ThreadGroup tg,Runnable run,String ThreadName);
new Thread(ThreadGroup tg,String ThreadName)
实例化线程组:
//当前的线程组从属于那一个线程组
new ThreadGroup(ThreadGroup tg,String ThreadName);
new ThreadGroup(String ThreadName)
方法:
currentThread():调用当对象的线程
enumerate(Thread[] tarray): 将当前线程的线程组及其子组中的每一个活动线程引用复制到指定的数组中。
enumerate(Thread[] tarray,false): 当前线程组内的每一个活动线程引用复制到数组中。
enumerate(ThreadGroup[] tarray): 将当前线程的线程组及其子组中的每一个活动线程引用复制到指定的数组中。
enumerate(ThreadGroup[] tarray,false): 当前线程组内的每一个活动线程组的引用复制到数组中。 getThreadGroup();返回该线程所属的线程组。
void interrupt(): 中断线程。
static boolean interrupted(): 测试当前线程是否已经中断。
boolean isAlive(): 测试线程是否处于活动状态。
boolean isDaemon():测试该线程是否为守护线程。
boolean isInterrupted():测试线程是否已经中断。
void join():等待该线程终止。
setPriority(int newPriority) :线程的优先级。
yield():暂停当前正在执行的线程对象,释放资源,并执行其他线程,将其放到线程队列的最后。
activeCount() :返回当前线程的线程组中活动线程的数目。
二、线程池:系统每产一个线程要占用较多时间,所以我们可以用线程,在池中预放n个线程,用时取出,不用时可以还回去。
三、基于线程的时间管理
四、线程实现死锁
四、线程的作用
进程内的并发
五、线程与进程的区别
共同点:
都是并发
不同点:
进程有独立的内存空间,线程没有独立内存空间。
进程间的通信耗费较大的资源,线程间的通信比较容易。
进程的并发是由线程实现的。
六、线程实现
1、继承于Thread类,覆盖run()方法,通过start启动线程,调用run方法。
2、实现Runnable接口,实现run()方法。实例化该类,通过构造子将其传到一个Thread对象中
,通过start启动线程,调用run方法
七、线程的同步
--脏读:后一个事务用了前一个事务没有确认的数据
事务1
update buy set amount=amount+100 where buyID=1;
rollback;
事务2:在事务1修改但没有执行rollback前,执行当前update交提交
update buy set amount=amount+100 where buyID=1;
commit;
--覆盖更新:后一个事务将前一个事务的更新覆盖了。
(update一读到就加锁)两个事务同时读取,因为事务1所在主机速度快先更新并提交,
然后事务2更新并提交。
事务1
update buy set amount=amount+100 where buyID=1;
commit;
事务2:在事务1修改但没有执行rollback前,执行当前update交提交
update buy set amount=amount+100 where buyID=1;
commit;
什么是线程同步:一个对象的一个同步方法被一个线程调用,另一个线程不管调用当前同步方法
还是其它的同步方法只能等待同步方法执行结束,调用非同步方法不影响。
实现方法:
public synchronized void a1()
{
}
在线程的run方法中说明是一个同步对象,调用这个对象的所有方法都是同步方法。
synchronized(co)
{
co.test1();
}
通讯的两种方式:
1、基于tcp/ip协议,用Sockect实现
特点:产生会话,稳定,每一次交互都有确认。
代码实现:
1、服务端:必须公开一个端口。
//产生服务器的实例
ServerSocket ss=new ServerSocket(4001);
//监听并到到一个会话
Socket socket=ss.listener();
//得到输入的信息
socket.getInputStream();
//向外输出信息
socket.getOutputStream();
2、客户端:通过ip+端口连接服务端
//连接服务端
Socket socket=new Socket("主机名/ip",4001);
//得到输入的信息
socket.getInputStream();
//向外输出信息
socket.getOutputStream();
2、基于udp协议,
特点:不产生会话,可以广播,可以批量发送。
DatagramSocket:发送接收信息
DatagramPacket:承载信息
数据库事务中的"脏读"."不可重复的读"及"虚读"
2008年02月21日 星期四 13:06
脏读 dirty reads:当事务读取还未被提交的数据时,就会发生这种事件。举例来说:Transaction 1 修改了一行数据,然后 Transaction 2 在 Transaction 1 还未提交修改操作之前读取了被修改的行。如果 Transaction 1 回滚了修改操作,那么 Transaction 2 读取的数据就可以看作是从未存在过的。
不可重复的读 non-repeatable reads:当事务两次读取同一行数据,但每次得到的数据都不一样时,就会发生这种事件。举例来说:Transaction 1 读取一行数据,然后 Transaction 2 修改或删除该行并提交修改操作。当 Transaction 1 试图重新读取该行时,它就会得到不同的数据值(如果该行被更新)或发现该行不再存在(如果该行被删除)。
虚读 phantom read:如果符合搜索条件的一行数据在后面的读取操作中出现,但该行数据却不属于最初的数据,就会发生这种事件。举例来说:Transaction 1 读取满足某种搜索条件的一些行,然后 Transaction 2 插入了符合 Transaction 1 的搜索条件的一个新行。如果 Transaction 1 重新执行产生原来那些行的查询,就会得到不同的行。
事务场景是这样的:
对于同一个银行帐户A内有200元,甲进行提款操作100元,乙进行转帐操作100元到B帐户。如果事务没有进行隔离可能会并发如下问题:
1、第一类丢失更新:首先甲提款时帐户内有200元,同时乙转帐也是200元,然后甲乙同时操作,甲操作成功取走100元,乙操作失败回滚,帐户内最终为200元,这样甲的操作被覆盖掉了,银行损失100元。
2、脏读:甲取款100元未提交,乙进行转帐查到帐户内剩有100元,这是甲放弃操作回滚,乙正常操作提交,帐户内最终为0元,乙读取了甲的脏数据,客户损失100元。
3、虚读:和脏读类似,是针对于插入操作过程中的读取问题,如丙存款100元未提交,这时银行做报表进行统计查询帐户为200元,然后丙提交了,这时银行再统计发现帐户为300元了,无法判断到底以哪个为准?
大家好像觉得统计这个东西肯定是时时更新的,这种情况很正常;但是如果统计是在一个事务中的时候就不正常了,比如我们的一个统计应用需要将统计结果分别输出到电脑屏幕和远程网络某台计算机的磁盘文件中,为了
提高性能和用户响应我们分成2个线程,这时先完成的和后完成的统计数据就可能不一致,我们就不知道以哪个为准了。
4、不可重复读:甲乙同时开始都查到帐户内为200元,甲先开始取款100元提交,这时乙在准备最后更新的时候又进行了一次查询,发现结果是100元,这时乙就会很困惑,不知道该将帐户改为100还是0。
和脏读的区别是,脏读是读取前一事务未提交的脏数据,不可重复读是重新读取了前一事务已提交的数据。
5、第二类丢失更新:是不可重复读的一种特例,如上,乙不做第二次查询而是直接操作完成,帐户内最终为100元,甲的操作被覆盖掉了,银行损失100元。感觉和第一类丢失更新类似。
在多个事务并发做数据库操作的时候,如果没有有效的避免机制,就会出现种种问题。大体上有三种问题,归结如下:
1、丢失更新
如果两个事务都要更新数据库一个字段X,x=100
事务A 事务B
读取X=100
读取X=100
写入x=X+100
写入x=X+200
事务结束x=200
事务结束x=300
最后x==300
这种情况事务A的更新就被覆盖掉了、丢失了。
丢失更新说明事务进行数据库写操作的时候可能会出现的问题。
2、不可重复读
一个事务在自己没有更新数据库数据的情况,同一个查询操作执行两次或多次的结果应该是一致的;如果不一致,就说明为不可重复读。
还是用上面的例子
事务A 事务B
读取X=100
读取X=100
读取X=100
写入x=X+100
读取X=200
事务结束x=200
事务结束x=200
这种情况事务A多次读取x的结果出现了不一致,即为不可重复读。
再有一情况就是幻影
事务A读的时候读出了15条记录,事务B在事务A执行的过程中删除(增加)了1条,事务A再读的时候就变成了14(16)条,这种情况就叫做幻影读。
不可重复读说明了做数据库读操作的时候可能会出现的问题。
3、脏读(未提交读)
防止一个事务读到另一个事务还没有提交的记录。
如:
事务A 事务B
读取X=100
写入x=X+100
读取X=200
事务回滚x=100
读取X=100
事务结束x=100
x锁 排他锁 被加锁的对象只能被持有锁的事务读取和修改,其他事务无法在该对象上加其他锁,也不能读取和修改该对象
s锁 共享锁 被加锁的对象可以被持锁事务读取,但是不能被修改,其他事务也可以在上面再加s锁。
封锁协议:
一级封锁协议:
在事务修改数据的时候加x锁,直到事务结束(提交或者回滚)释放x锁。一级封锁协议可以有效的防止丢失更新,但是不能防止脏读不可重复读的出现。
二级封锁协议:
在一级封锁的基础上事务读数据的时候加s锁,读取之后释放。二级封锁协议可以防止丢失更新,脏读。不能防止不可重复读。
三级封锁协议:
在一级封锁的基础上事务读数据的时候加s锁,直到事务结束释放。二级封锁协议可以防止丢失更新,脏读,不可重复读
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -