线程是什么
进程是对CPU的抽象,而线程更细化了进程的运行流程
先看一下这个图
线程和进程的关系有
- 进程中就是线程在执行,所有(主)线程执行完了进程也就结束了
- 多个线程从1秒钟是同时运行完成,从1纳秒(或是更小的单位)看是排队执行
- 在不断运行的进程中(没有IO操作),使用多个线程并没有提高进程的执行效率,还有所降低
为什么需要多线程
我第一次使用线程是在学习BIO和多线程进行通信时
服务端每次接收到一个客户端的请求就创建一个线程(虽然可以使用tomcat和Netty大大优化了BIO,但它们也使用了多线程)
就这个用法而言,难道不能用多进程代替多线程进行通信吗(不知道提出这个问题是不是太笨了)
我的理解是:
- 多个客户端和服务端通信时,服务端有很多资源是多个客户端共用的
- 如果使用多进程,也就是运行很多个服务端,相同服务端就得存放相同的资源在电脑内存中,太浪费空间了(如果是为了防止故障存在多台机器肯定就不浪费)
- 所以创建了线程这个概念去共用进程的资源
线程需要什么
业务场景还是在通信中
- 服务端线程需要在客户端发消息过来时及时响应,至于怎么响应肯定就写在自己的代码逻辑里
- 所以线程需要一段可以执行的代码块
- A发消息,就得切换到与A通信的线程,B发消息,就得切换到与B通信的线程,线程就得不停的切换
- 所以CPU得知道哪个线程和A通信等,就得给线程个ID好辨认
总之线程还需要各种各样我没学过的东西。
下面根据自己的理解,实现一个简单的用户线程
用户线程:如果里面有一个线程执行不下去了(等待输入、运行报错、死循环),操作系统并不认识用户线程,只认识用户进程,会让整个用户进程执行不下去
实现一个简单的用户线程
线程的数据结构
MyThread.java
1 import java.lang.reflect.Constructor; 2 import java.lang.reflect.InvocationTargetException; 3 import java.lang.reflect.Method; 4 5 public class MyThread<T> { 6 7 private int Id;//区分各个线程的标识符 8 private int runTime;//线程当前可以运行的时间片 9 private int totalTime; //线程总共需要运行的时间 10 private int priority;//线程的优先级 11 //交给线程执行的代码块,用一个对象参数替代 12 public T data; 13 14 public MyThread() { 15 } 16 17 public MyThread(int id, int totalTime,int priority, T data) { 18 19 this.Id = id; 20 // this.runTime = runTime; 21 this.totalTime = totalTime; 22 this.runTime = totalTime/2;//固定线程每次运行时间片是总时间的二分之一,方便测试 23 this.priority = priority; 24 this.data = data; 25 } 26 27 public void run(){ 28 29 try { 30 31 System.out.println("线程id:"+Id); 32 System.out.println("剩余时间片:" + runTime); 33 System.out.println("线程优先级:"+priority); 34 35 //使用java的反射机制,执行代码块的内容 36 Class<?> clazz = data.getClass(); 37 Constructor constructor = clazz.getDeclaredConstructor(); 38 Object object = constructor.newInstance(); 39 40 Method method = clazz.getMethod("hello"); 41 method.invoke(object); 42 43 //每执行一次,线程总时间减少 44 //运行的时间片不变 45 totalTime-=runTime; 46 // 判断线程运行总时间是否快要结束了 47 runTime = Math.min(totalTime,runTime); 48 49 } catch (NoSuchMethodException e) { 50 e.printStackTrace(); 51 } catch (InvocationTargetException e) { 52 e.printStackTrace(); 53 } catch (InstantiationException e) { 54 e.printStackTrace(); 55 } catch (IllegalAccessException e) { 56 e.printStackTrace(); 57 } 58 59 } 60 61 public int getPriority() { 62 return priority; 63 } 64 65 public int getRunTime() { 66 return runTime; 67 } 68 69 public int getTotalTime() { 70 return totalTime; 71 } 72 }
写一个类代替代码块供线程执行
1 public class MyMethod { 2 3 public MyMethod() { 4 5 } 6 7 public void hello(){ 8 System.out.println("hello"); 9 } 10 11 }
测试线程的执行
1 import java.util.Comparator; 2 import java.util.PriorityQueue; 3 public class Test { 4 5 public static void main(String[] args) { 6 7 //由优先队列进行线程的调度,即优先级高的线程先执行,优先级相同采用先入先出算法 8 PriorityQueue<MyThread> priorityQueue = new PriorityQueue<>(new Comparator<MyThread>() { 9 @Override 10 public int compare(MyThread o1, MyThread o2) { 11 return o2.getPriority() - o1.getPriority(); 12 } 13 }); 14 15 MyMethod method = new MyMethod(); 16 //创建三个线程 17 MyThread<MyMethod> myThread = new MyThread<>(1,3,5,method); 18 MyThread<MyMethod> myThread1 = new MyThread<>(2,3,5,method); 19 MyThread<MyMethod> myThread2 = new MyThread<>(3,2,10,method); 20 //线程进入队列 21 priorityQueue.offer(myThread); 22 priorityQueue.offer(myThread1); 23 priorityQueue.offer(myThread2); 24 25 //在循环中不断的执行线程 26 27 while (!priorityQueue.isEmpty()){ 28 29 MyThread<MyMethod> myThreadRun = priorityQueue.poll(); 30 31 myThreadRun.run(); 32 33 //线程总时间不为0,进入队列等待下次执行 34 if (myThreadRun.getTotalTime() != 0){ 35 priorityQueue.offer(myThreadRun); 36 } 37 } 38 39 System.out.println("线程都执行完了,进程也就结束了"); 40 } 41 }
执行结果
总结
- 这个用户线程的例子实现的不好
- 例子中总共有四个线程,主线程担任了一个调度线程的职责
- 在操作系统中,线程的执行
- 操作系统会不断主动去询问线程的时间片结束没,一旦结束就将线程换下
- 多线程排队执行出现竞争
- 一个线程执行一个打印命令,对应了CPU中的多条指令
- 如果指令没执行完,切换到了其他线程,再换回来执行指令,就会出现一种竞争的情况