TCP通信,多线程基础
什么是进程
-进程是操作系统中运行的一个任务(一个应用程序运行在一个进程中)。
-进程是一块包含了某些资源的内存区域。操作系统利用进程把它的工作划分为一些功能单元。
什么线程
-进程中包含的一个或多个执行单元称为线程(thread)。进程还拥有一个私有的虚拟地址空间,该空间仅能被它说包含的线程访问。
-线程只能归属于一个进程并且它只能访问该进程所拥有的资源。当操作系统创建一个进程后,该进程会自动申请一个名为主线程或首要线程的线程
线程的使用场合
-线程通常用于在一个程序中需要同时完成多个任务的情况。我们可以将每个任务定义为一个线程,使他们得以一同工作。
-也可以用于在单一线程中可以完成,但是使用多线程可以更快的情况。比如下载文件
并发原理
多个线程“同时”运行。这只是感官上的表现,其实是多个线程时,每个线程做一点,走走停停,不是绝对意义上的“同时发生”。
多线程
多线程改变了代码的执行方式,从原有的所有代码都是串行操作改变为多个代码片段之间并行操作。因此多线程运行多个代码片段“同时运行”。
多线程的两种创建方式:
第一种:
继承线程(Thread 不是抽象类可以实例化)并重写run方法,在run方法中定义线程要执行的任务
public class Thread{
public static void main(String[] args){
Thread t = new MyThread();
t.start();
}
}
class Mythread extends Thread{
public void run(){
//运行代码
}
}
启动线程时调用线程的start方法,而不是直接调用run方法
。当线程的start方法调用后,线程纳入到线程调度中,当其第一次分配到时间片开始运行时,它的run方法会自动被执行
第一种创建线程的方式0:
优点:创建简单方便。
缺点:1、由于java是单继承的,这导致继承了线程就无法在继承其他的类,这会导致无法重用其他超类的方法而产生继承冲突问题
2、定义线程的同时重写run方法,这就等于规定了线程要执行的具体任务,导致线程与其他执行的任务产生必然的耦合关系,不利于线程的重用
第二种:
实现Runnable接口,并重写run方法来单独定义线程的任务
public class Thread{
public static void main(String[] args){
//单独实例化
Runnable r = new MyRunnable();
r.start();
//创建线程
Thread t = new Thread(r);
t.start();
}
}
class MyRunnable implements Runnable{
public void run(){
}
}
优点:接口可以实现多个
使用匿名内部类的创建线程
public class Thread{
public static void main(String[] args){
//第一种:
Thread t1 = new Thread(){
public void run(){
}
};
//第二种:
Runnable r = new Runnable(){
public void run(){
}
};
Thread t2 = new Thread(r);
t1.start();
t2.start();
}
}
获取主线程的方法
获取主线程currentThread()
线程提供的一个静态方法,该方法可以获取运行这个方法的线程
实际上,java的所有代码都是靠线程运行的,main方法也不例外。运行main方法的线程不是由我们创建的,而是JVM自行创建,并用来运行main方法。我们通常称这个线程为“主线程”
Thread main = Thread.cuurrentThread();
System.out.println("运行main方法的线程是:"+main);
获取线程的名字getName()
String name = main.getName();
获取唯一标识getId()
long id = main.getId();
获取线程的优先级getPriority()
int priority = main.getPriority();
判断线程是否活着isAlive()
boolean isAlive = main.isAlive();
判断是否是守护线程isDaemon()
boolean isDaemon = main.isDaemon();
判断线程是中断了inInterrupted()
boolean inInterrupted = main.inInterrupted();
线程的优先级
线程启动后就纳入到了线程调度中统一管理,什么时候获取CPU时间片完全取决于线程调度,线程是不能主动索取的,通过调整线程的优先级,可以最大程度的干涉分配CPU时间片的几率。
理论上线程优先级越高的线程获取CPU时间片的几率越高
线程的优先级有10个等级,用整数1-10表示,1是最小,5是默认,10是最高
Sleep()方法
线程提供的一个静态方法,该方法可以让运行这个方法的线程处于阻塞状态指定的毫秒,超过后线程会自动回到Runnable状态再次并发运行
eg:
public class SleepThread{
public static void main(String[] args){
System.out.println("程序开始了");
try{
Thread.sleep(5000);//程序沉睡5000毫秒
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("程序结束了");
}
}
sleep方法要求处理中断异常
interrupt()方法
当一个线程调用sleep方法处于阻塞状态的过程中,此时被其他线程调用了该线程的interrupt方法,那么就会打断这个线程的睡眠阻塞,测试sleep方法就会抛出中断异常告知
public class SleepDemo2 {
public static void main(String[] args) {
Thread huang = new Thread(){
public void run(){
System.out.println("煌:刚做完作业,睡一会");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
System.out.println("煌:干嘛呢,干嘛了");
}
System.out.println("徐:醒了");
}
};
Thread xu = new Thread(){
public void run(){
System.out.println("徐:开始做作业");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
System.out.println("哐当");
System.out.println("徐:李总,搞定!");
//中断huang的阻塞状态
huang.interrupt();
}
};
huang.start();
xu.start();
}
}
守护线程setDaemon()
守护线程也称为后台线程,创建和使用上与前台线程一样,但有一点不同:
当进程结束时,所有正在运行的守护线程都会被强制停止。
进程的结束:当所有普通线程都结束时,进程结束。
进程里面至少要有一条普通线程,如果一天普通线程都没有,进程将会结束
//设置为守护线程要在线程启动之前调用setDaemon方法
t.setDaemon(true);
t.start();
一定要在线程启动之前调用
main方法执行完,程序不一定结束,程序结不结束取决于进程结不结束。进程结不结束取决于普通线程是否结束。
阻塞join()方法
join方法允许当前线程在join方法所属线程上等待,直到该线程结束后,结束join阻塞继续后续操作。
所以 join 方法可以协调线程的同步运行。
同步运行:多个线程之间执行有顺序。
异步运行:多个线程之间各自执行各自的。
public class JoinDemo {
private static boolean isFinish = false;
public static void main(String[] args) {
/*
* 当一个方法的局部内部类中引用了这个方法的其他局部变量时,该变量必须声明为final的
* JDK8之后可以不写final,但是该变量依然会被编译器最终改为final的,这是源自JVM的内存分配问题
*/
Thread download = new Thread(){
public void run(){
System.out.println("down:开始下载图片");
for(int i =1;i<=100;i++){
System.out.println("down:"+i+"%");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
}
System.out.println("down:图片下载完毕!");
isFinish = true;
}
};
Thread show = new Thread(){
public void run(){
try {
System.out.println("show:开始显示文字");
Thread.sleep(3000);
System.out.println("show:文字显示完毕!");
System.out.println("show:开始显示图片");
/*
* 先等待download线程执行完毕(图片下载完)之后再继续后续工作
*/
download.join();
if(!isFinish){
throw new RuntimeException("图片加载失败!");
}
System.out.println("show:显示图片完毕!");
} catch (Exception e) {
e.printStackTrace();
}
}
};
download.start();
show.start();
}
}
sleep()方法的阻塞需要规定时间,而join方法会等待某个线程执行完后再执行后面的代码.