java多线程
当有多个客户端请求连接服务端时,这时服务端应采取多线程进行处理,每接收到一个请求,就创建一个线程处理此请求。
ServerSocket serverSocket=new ServerSocket(8888);
Socket socket=null;
while(true){
socket=serverSocket.accept();
ServerThread serverThread=new ServerThread(socket);
serverThread.start();
}
-----------------------------------------------------------------------------------------
ServerThread extends Thread{
Socket socket;
public ServerThread( Socket socket){
this.socket=socket;
}
public void run(){
socket.getInputStream();
......
}
}
--------------------------------------------------------------------------------------------
还有一种创建多线程的方法:如果类继承了某个类,这时就只能通过实现Runnable接口来创建了。
A implements Runnable{
public void run(){
.....
}
}
为了启动A,需要首先实例化一个Thread,并传入A实例:
Thread thread=new Thread(a);
thread.start();
----------------------------------------------------------------------------------------------------------------------------
查看Thread类的源码,可以看到,该类实现了Runnable接口,成员变量有一个为Runnable target,构造方法Thread(Runnable target)会调用Thread类的init()方法,在init()方法体中赋予值:this.target=target。Thread
类有一个run()方法,该方法判断是否有target,如果有就调用target.run;如果没有,就执行自身的run方法,但是
自身的run()方法什么内容都没有,所以就需要覆写run()方法。
因此,创建一个线程的两种实现方式分别为:
Thread thread=new Thread(){
@Override
void run(){
//执行代码
}
};
thread.start();
Thread thread1=new Thread(new Runnable(){
@Override
void run(){
//执行代码
}
});
thread1.start();
------------------------------------------------------------------------------------------------------------------------
多线程真的能够提高程序效率吗?如果对于单核CPU而言,它要同时执行好几个线程,那么线程之间的切换
需要消耗部分时间,而且线程的同步使用锁机制,频繁的加锁和释放锁也很大的性能开销。
网络上采用多线程下载视频 速度非常快,这是为什么呢?如果只有一个线程从服务器下载资源,服务器分配给该线程
的带宽为20,但是假设有10个线程同时从服务器下载资源,服务器分配的带宽就为200了。
—————————————————————————————————————————
定时器Timer:该类可以实现一个定时器的功能,即多少秒执行一次任务。
new Timer().schedule(new TimerTask(){
@Override
public void run(){
//该线程执行某项任务
}
},3000);
这个TimerTask就是一个线程,实现了Runnable接口。
---------------------------------------------------------------------------------------------------------------------------------
如果多个线程在同一时间对同一个对象进行操作,就会产生线程安全问题。a线程中的方法还没有执行完,
又去执行b线程中的方法了(cpu在线程之间进行不规律的切换),结果成员变量的值就会不一致了。
解决方法:利用synchronized给对象加锁
每个对象的对象头都有一把锁,只有当线程获取到该对象的锁之后,才能执行对象中的方法。
有三种加锁机制(synchronized只能对方法加锁):
public synchronized void foo(){
}
public synchronized static void foo1(){
}
public void foo3(){
synchronizde(this){
}
}
public synchronized void foo4(){
}
public void foo5(){
}
a线程要执行foo()方法,必须获得对象的锁,获得到了就进来执行,此时如果b线程访问foo()或者foo4(),都得先获取对象的锁,但是此时对象的锁还没有释放,所以就不能访问到。但是如果此时b线程访问foo5(),由于访问foo5()时并不需要先获得锁,所以b线程可以随时访问到。
如果对类方法foo1()加锁,那么必须首先获得类对象的锁才能访问,此时如果b线程访问foo1(),依然能够访问到,因为它们加的锁不一样,一个是对类对象加的锁,一个是对类的实例对象加的锁。
---------------------------------------------------------------------------------------------------------------------------------
线程同步:a线程在运行的时候,b线程需要等待(wait),待到a线程运行到一定时候,会改变信号量,停止当前线程的执行,并唤醒b线程(notify);b线程唤醒之后,会执行相关代码(此时a线程处于wait()状态),b执行一段时间后,又改变信号量,并唤醒a线程。
java.lang.Object类有wait()方法和notify()方法,其他类都是Object的子类,因此任何一个类都有wait()方法和notify()方法。
信号量为boolean类型,在if或者while(可以预防假唤醒)语句中判断是否能够执行,但是在执行之前必须被唤醒。
wait()和notify()方法必须用到同步代码块中,即方法中必须有synchronized同步锁。当执行this.notity()方法时,对象将锁抛出,其他线程可以竞争锁。
-------------------------------------------------------------------------------------------------------------------------------------
线程范围内的共享变量:A、B两个模块在a线程中共享同一变量,在b线程中共享另一(同一)变量。变量在线程内共享,但在线程间相互独立。
ThreadLocal类用于解决线程范围内的共享变量问题。该类位于java.lang.ThreadLocal中,ThreadLocal类相当于一个Map中的一个Entry(数据结构为<Thread,data>,ThreadLocal类的T get()方法可以获得当前线程的共享变量,set(T value)可以设置当前线程的共享变量值),不同的线程享用不同的data,但是该data在同一个线程内共享。
应用场景:在数据库的事务中,线程首先开启事务(getConnection.startTranction),然后执行操作,最后提交事务(getConnection.commit)。如果张三给李四转账,将使用同一个数据库连接connetion。假设在这个时候王五给赵六转账,也使用同一个connection,那么当王五connetion.commit的时候张三给李四转账的事务也跟着被提交了。所以这两个事务需要使用不同的数据库连接对象connection.
-----------------------------------------------------------------------------------------------------------------------------------------
Tomcat服务器中有一个连接池,客户端每发送一个Http请求,tomcat就调用一个线程处理该请求,该线程中的数据是线程范围内的共享数据,其他线程(连接请求)不能访问。连接池中存放的即是一个个ThreadLocal对象,每个对象包含了当前线程和data数据。如果有成千上万的请求访问该Tomcat服务器,就会创建出成千上万的ThreadLocal进行处理,那么ThreadLocal就必须要有 remove ()方法删除线程中的数据,释放掉内存,释放完之后线程又回到了线程池中,等待下一个连接。
数据库的单例模式:每当一个客户端连接一个服务器端的时候,就会创建一个connection连接对象与数据库进行交互。如果有connection对象,就不创建,否则创建一个connection对象,当下次同一个客户端再次连接的时候,就不需要再次创建connection对象了。