多线程
进程、线程、多线程
引子: 对于大量问题,我们想把问题切分成多个可独立运行的部分(任务),从而提高程序的响应能力。在程序中,这些彼此独立运行的部分称之为线程,上述概念被称为‘并发”。例如当你点击用户界面的按钮时会快速得到响应,而不是等其他程序执行完才响应。最初,程序员用机器底层知识来编写这样的程序,但其难度太大且不能移植,后来就出现了多线程
并行性:并行指在同一时刻,有多条指令在多个处理器上同时执行
并发性:并发指在同一时刻只能有一条指令执行,但多个进程指令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果。
程序、进程和线程的区别:
Ø 几乎所有操作系统都支持同时运行多个程序,一个程序可以启动多个进程。当程序进入内存运行时,即变成进程。进程是计算机执行程序的过程,是一个正在执行的程序。进程的特征如下
q 独立性:进程是系统中独立存在的实体,它拥有自己一组独立的资源和私有的地址空间。在没有经过进程本身的允许 下,一个进程不可以访问其他进程。
q 动态性:程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合。进程具有自己的生命周期和各种不同的状态。
q 并发性:多个进程可以在单个处理器上并发执行,多个进程之间不会互相影响。
Ø 线程是进程的执行单元。每个线程都是独立的栈内存,可以独立运行,相互之间没有关系。
q 线程是进程的组成部分,这些线程共享进程的全部资源。线程控制着进程的执行,是进程的运行控制单元。线程的调度和管理由进程本身负责完成。
q 线程用于完成一定的任务,相互之间协同来完成进程所要完成的任务。线程之间通过共享的进程数据进行通信
q 在java中,程序通过流控制来执行程序流。程序中单个顺序的流控制称为线程
Ø 线程与进程的主要区别在于:每个进程都需要操作系统为其分配独立的内存地址空间,而同一进程中的所有线程在同一块地址空间中工作,这些线程可以共享同一块内存和系统资源
线程补充
Ø 主线程: 当Java程序启动时,一个线程立刻运行,该线程通常叫做程序的主线程(main thread),它是程序开始时就执行 的 。它是产生其它子线程的线程,通常它必须最后完成执行,用于执行各种关闭动作。
Ø 线程池:线程池在系统启动时即创建大量空闲的线程。程序将一个Runnable对象传给线程池,线程池就会启动一条线程来执行该对象的run方法,当run方法执行结束后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个Runnable对象的run方法。此外,线程池可以有效地控制系统中并发线程的数量,防止并发线程过多而导致系统性能剧烈下降,甚至导致JVM崩溃
Ø 多线程:就是单个进程中可以同时运行多个不同的线程,执行不同的任务。
因为计算机中机器指令的真正执行者是CPU线程必须获得CPU的使用权,才能执行一条指令。导致了多线程特性:随机性谁抢到谁执行,至于执行多长,cpu说了算。多线程编程优点如下:
q 进程间不能共享内存,而线程可以。
q 系统创建进程需要为该进程重新分配系统资源,但创建线程则代价小得多,因此使用多线程来实现多任务并发比多进程的效率高。
q Java语言内置多线程功能支持,而不是单纯地作为底层操作系统的调度方式,从而简化了Java的多线程编程。
Ø 我们可以创建其他线程,和主线程并发运行
开启、停止线程
开启线程的方法:
1. 继承Thread类和实现Runnable接口,并重写run方法,最后调用start()开启线程
2. 当线程开始,run()方法里代码被执行
3.开启方式的区别:.继承方式线程代码存放在Thread子类run方法中,实现方式代码存放在接口的子类run方法中。
实现方式避免了单继承的局限性
4.run方法与start方法的区别:
Ø 必须调用start才能开启线程,run方法不可以。start方法用于启动线程,而run方法包含线程运行时的任务。而直接调用线程的run方法,会被当成一般方法执行,而不是线程执行体。
Ø 一个线程只能启动一次。当新建线程对象多次调用start()或对处于死亡状态的线程调用start()方法,将引发java.lang.IllegalThreadStateException
如何停止线程?
Ø 只有一种方式,就是run方法结束.所以当开启多线程运行时, 只要定义一个标识变量,通过改变它的值来控制住循环(run方法里通常是循环结构),就可以让run方法结束。
public class Runner extends Thread{
private boolean flag=false;
public void shutDown(){
flag=true;
}
public void run(){
while(!flag){
//your business logic
}
}
}
Thread的方法
Ø Thread.currentThread()----------------->返回当前线程对象
Thread.activeCount()------- >返回线程组中目前活动的线程的数目
Thread:setName(String name)-------- >为线程取名
Thread:getName()----------------------->返回线程名
Thread.yield()当前线程放弃cpu执行权。当前线程会停一会,具体多少时间不确定
Ø 线程操作方法
1、start()启动线程
2、sleep(int ms) 线程休眠
3、run()线程的执行内容
4、interrupt()线程强行中止休眠状态,进入下面程序的执行。
5、stop()终止线程(已取消,不要使用)
6、线程优先级控制setPriority(int p)getPriority()
线程的优先级是说执行到的机率会大一点,线程优先级是从1到10的。不同操作系统的优先级不同,而且不能很好地和Java的10个优先级对应,所以我们应尽量避免直接为线程指定优先级,而应使用MAX_PRIORITY, MIN_PRIORITY和NORM_PRIORITY三个静态常量来设置优先级,以保证程序的可移植性。
7、join()停止当前线程(不是所有线程),直到调用join方法的线程结束
8、currentThread() 获得当前运行的线程的引用
9、isAlive()测试线程是否处于活动状态
10. setDaemon() 守护线程,其实跟其他线程一样,只是守护线程会随着前台线程的结束而结束。
后台线程特点:开启后与前台线程共同抢夺cpu执行权并运行,但是当所有前台线程结束时它才自动结束
setDaemon(true)必须在start()方法之前调用,否则会引发I1legalThreadStateException异常
sleep和yield方法的区别:
Ø sleep方法暂停当前线程后,会给其他线程执行机会,不会理会其他线程的优先级。但yield方法只会给优先级相同,或优先级更高的线程执行机会。
Ø sleep方法会将线程转入阻塞状态,直到经过阻塞时间才会转入就绪状态。而yield不会将线程转入阻塞状态,它只是强制当前线程进入就绪状态。因此完全有可能某个线程调用yield方法暂停之后,立即再次获得处理器资源被执行。
Ø sleep方法会抛出InterruptedException异常,而yield方法不会抛出任何异常。
Ø sleep方法比yield方法有更好的可移植性,通常不要依靠yield来控制并发线程的执行。
线程的状态和相关方法
线程的五种状态:
新建状态:用new语句创建的线程对象处于新建状态,仅在堆区中分配了内存
就绪状态:当一个线程对象创建后,其他线程调用它的start方法,该线程就进入就绪状态,处于这个状态的线程位于可运行池中,等待获得CPU的使用权
运行状态:当线程抢到cpu的运行资格和运行权的时候就可以运行了。一般只有一个CPU,因此只有一个线程处于运行状态。只有处于就绪状态的线程才有机会转到运行状态
阻塞状态:当cpu有线程运行时,那种有运行资格而没有运行权的线程会在临时阻塞的地方等待,直到有运行权。
阻塞状态有三种:
Ø 位于线程等待池:当线程处于运行状态时,如果执行了wait()方法,jvm就会把线程放到等待池中
Ø 位于对象锁池中:当线程处于运行状态,试图获得某个对象的同步锁时,如果该对象的同步锁已经被其他线程占用,jvm就会把这个线程放到这个对象的锁池中
Ø 其他阻塞状态:当前线程执行了sleep()方法,或调用了其他线程的join()方法,或发出了I/O请求时,就会进入这个状态。当发出一个I/O请求时,该线程会放弃CPU,进入阻塞状态,直到I/O处理完毕,该线程才会恢复运行。
死亡状态:当线程退出run()方法自然结束或遇到异常结束。
定时器Timer
Timer表示定时执行特定的任务
Timer类的schedule(TirnerTask task, long delay, long period)方法用来设置定时器需要定时执行的任务。task参数表示任务;delay参数表示延迟执行的时间;period参数表示每次执行任务的间隔时间
TimerTask类是一个抽象类,它实现了Runnable接口,它的run()方法表示定时器需要定时完成的任务
线程的同步
Ø 线程的职责就是执行一些操作,而多数操作都涉及到处理数据。当两个以上的线程需要共享数据,它们需要某种方法来确定数据在某一刻仅被一个线程占用。达到此目的的过程叫做同步(synchronization),同步是为了保证数据完整性
Ø 当一个线程已经在操纵共享资源时,其他线程只能等待,只有当己经在操纵共享资源的线程执行完同步代码块后,其他线程才有机会操纵共享资源。
Ø 线程同步也会降低程序并发性能。所以在使用同步时,应只在有必要的时候(通常在修改数据时)使用,且同步块应尽量短, 避免死锁
同步代码块.
Ø 只让一个线程执行完多条操作共享数据的语句。该线程在执行过程中,其他线程不会参与执行
同步的前提:1.必须有二个以上的线程
2.必须是多个线程使用同一个锁
3.必须保证同步中只能有一个线程在运行
Ø 同步的用法:
1.明确哪些是多线程运行代码
2.明确哪些是共享数据
3.明确多线程运行代码中哪些语句是操作共享数据的
synchronized(对象)
{
需要被同步的代码
}
(对象)如同锁,持有锁的线程可以在同步synchronized中执行,未持有锁的线程即使获得cpu执行权,也进不去
Ø 好处:解决了多线程的安全问题。弊端:多个线程需要判断锁,较为消耗资源
Ø synchronized声明不会被继承。如果一个用synchronized修饰的方法被子类覆盖,那么子类中这个方法不再保持同步,除非也用synchronized修饰。
锁
Ø 由于等待锁的线程只有在获得这把锁之后, 才能恢复运行, 所以让持有锁的线程在不再需要锁时及时释放是很重要的.
释放锁的情况
1.执行完同步代码块, 就会释放锁.
2.在执行同步代码块的过程中, 遇到异常(指没有手动抛出的异常)而导致线程终止, 锁也会被释放
3.在执行同步代码块的过程中, 执行了锁所属对象的wait()方法, 这个线程会释放锁, 进入线程等待池.
不会释放锁的情况
1.在执行同步代码块的过程中, 执行了Thread.sleep()方法, 当前线程放弃CPU,开始睡眠, 在睡眠中不会释放锁.
2.在执行同步代码块的过程中, 执行了Thread.yield()方法, 当前线程放弃CPU,但不会释放锁.
死锁:当一个线程等待由另一个线程持有的锁,而另一个线程正在等待已被第一个线程持有的锁时,就会发生死锁。
同步与锁的区别?
1, 同步升级以后把他封装成了对象,分成了加锁和解锁
2, 同步不能区分唤醒的是本方的线程还是对方的线程,而锁能够区分出来。原因是
一个锁里可以定义多个唤醒线程的监视器,给自己定义一个,对方定义一个
这样的话就可以唤醒定义监视器的线程了。
3, 同步只要用同步函数或者同步代码块就行了,但锁的话要加锁,如果线程出了问题
那么没解锁的话别的线程也进不去这样就成死锁了,所以一般加lock的话要用
Try{}lfinally{},这样的话如果线程了中断了那么也一定会执行解锁的程序。
4, 同步的对象是任意的,所以方法是Object的,但是锁是lock接口的方法,所以
锁可以用的方法都是在继承了lock接口的子类之中。
jdk1.5以后将同步和锁封装成了对象。
并将操作锁的隐式方式定义到了该对象中,将隐式动作变成了显示动作。
Lock接口:出现替代了同步代码块或者同步函数。将同步的隐式锁操作变成现实锁操作。
同时更为灵活。可以一个锁上加上多组监视器。lock():获取锁。unlock():释放锁,通常需要定义finally代码块中。
Condition接口:出现替代了Object中的wait notify notifyAll方法。
将这些监视器方法单独进行了封装,变成Condition监视器对象。可以任意锁进行组合。
await();
signal();
signalAll();
线程的通信
引子:不同的线程执行不同的任务,如果这些任务有某种联系,线程之间必须能够通信,协调完成工作。例如生产者和消费者
java.lang.object类中提供了用于线程通信的方法
Ø wait():执行该方法的线程释放对象锁和CPU的占用,jvm把该线程放到该对象的线程等待池中。该线程等待其他线程将它唤醒。
Ø notify():执行该方法的线程随即唤醒在对象的线程等待池中等待的一个线程,jvm会把它转到对象的锁池中。
Ø notifyAll():执行该方法的线程会唤醒对象的等待池中的所有线程,jvm会把它们都转到对象的锁池中。
Ø sleep() wait() notifyAll()的差异?
这些方法必须定义在共用同一把锁的同步代码块中,因为是用于操作线程状态的方法。使用时必须要明确到底操作的是哪个锁上的线程。sleep()在锁中等待时是不释放锁的,而wait()在等待时会释放锁。notifyAll()会唤醒在同一个锁内,所有在线程池中等待的线程
线程的通讯:就是多个线程在操作同一资源,但是操作的动作不同
Ø 多线程运行机制:所有可运行线程根据优先级保存在池中,当一个被阻塞的线程变成可运行时,它会被放入相应的可运行池,可运行池中的线程都得到执行机会,一般优先级最高的线程机会最大。
Ø New一个线程之后,该线程对象不会表现出任何线程的动态特征,程序也不会执行线程的执行体。线程对象调用了start()方法之后,该线程就处于就绪状态,但该线程并没有开始运行,它只是表示该线程可以运行了。
面试题:写一个死锁程序
就是同步中嵌套同步
class Mylock
{
static Object locka=new Object();
static Object lockb=new Object();
}
class Test
{
private boolean b;
Test(boolean b)
{
this.b=b;
}
public void run()
{
if(b)
{
while(true)
{
synchronized(Mylock.locka)
{
System.out.printIn("if locka");
synchronized(Mylock.lockb)
{
System.out.printIn("if lockb");
}
}
}
}
else
{
while(true)
{
synchronized(Mylock.lockb)
{
System.out.printIn("else lockb");
synchronized(Mylock.locka)
{
System.out.printIn("else locka");
}
}
}
}
}
}
网络编程
网络
网络:就是把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大、功能强的网络系统,从而使众多的计算机可以方便地互相传递信息,共享硬件、软件、数据信息等资源。
Ø 计算机网络是现代通信技术与计算机技术相结合的产物
Ø 分为局域网(LAN)、城域网(MAN)、广域网(WAN)
Ø 计算机网络可以提供资源共享,信息传输与集中处理,均衡负荷与分布处理,综合信息服务等主要功能。
Internet :是网络的最典型例子。是一个全球的、巨大的网络集合,用于Internet中不同计算机系统的互联通信。
网络模型
l TCP/IP参考模型
TCP/IP:即传输控制协议/网际互联协议,是Internet的基础。只有TCP和IP两者的结合,才能保证Internet在复杂的环境下正常运行,因此常把这两个协议统称作TCP/IP协议。IP获取地址而TCP确保数据送达该地址。
通常分为四层:
应用层向用户提供一组应用程序,如电子邮件、文件传输访问、远程登录等。这一层有着很多协议来支持不同的应用,如万维网访问用HTTP协议、文件传输用FTP、电子邮件发送用SMTP、域名解析用DNS协议、远程登录用Telnet协议等。对用户而言,看到的是由一个个软件所构成的、大多为图形化操作的界面,而后台实际上运行的便是这些协议。
传输层的功能主要是提供应用程序间的通信,包括格式化信息流和提供可靠传输等。这一层的协议有TCP(Transmission Control Protocol,传输控制协议)和UDP(User Datagram Protocol,用户数据报协议)
网络层是非常关键的一层,负责相邻计算机之间的通信,主要定义了IP地址格式,从而使得不同应用类型的数据在Internet上通畅地传愉。IP协议就是一个网络层协议。
网络接口层是最底层,负责接收IP数据包并通过网络进行发送,或者从网络上接收物理帧,抽出IP数据报,交给IP层。
网络通信原理:两台计算机通信,双方必须有唯一的地址。网络通信必须有硬件和软件方面的支持
l 网络通讯要素:IP地址,端口号,传输协议
IP地址:Internet Protocol
• 用于唯一标识网络中的一个通信实体(通常是计算机)。
• IP协议:又称互联网协议,是支持互联网间互连的数据报协议。IP协议负责将消息从一个主机传送到另一个主机,消息在传送的过程中被分割成一个个的小包。
• IP地址 = 网络ID(标识所在网段)+主机ID(标识本机)
• 不易记忆,可用主机名主机名:localhost
• 本地回环地址:127.0.0.1,用于本机测试 特殊IP地址:0.0.0.0:本机
端口号
• 用于标识进程的逻辑地址。一个进程可以有多个端口号
• 端口是程序与外界通信的出入口
• 有效端口:0~65535,其中0~1024系统使用或保留端口。
传输协议
• 通讯的规则
• 常见协议:TCP,UDP
Ø UDP(User Datagram Protocol用户数据包协议)
• 将数据及源和目的封装成数据包中,不需要建立连接
• 每个数据报的大小在限制在64k内
• 因无连接,是不可靠协议
• 不需要建立连接,速度快
Ø TCP(Transmission Control Protocol 传输控制协议)
• 建立连接,形成传输数据的通道。
• 在连接中进行大数据量传输
• 通过三次握手完成连接,是可靠协议
• 必须建立连接,效率稍低
路由交换设备:将数据从一台计算机送到另一台计算机