线程、进程、多线程
一个进程可以有多个线程,如一个视频,可以同时听声音、看图像、看弹幕等等。
- 说起进程,就不得不说下程序。程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
- 而进程则是执行春哥徐的一次执行过程,它是一个动态的概念。是系统资源分配的单位。
- 通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程是CPU调度和执行的单位。
注意:很多多线程是模拟出来的,真正的多线程是指有多个CPU,即多核,如服务器。如果是模拟出来的多线程,即在一个CPU的情况下,在同一个时间点,CPU只能执行一个代码,因为切换得很快,所以就有了同时执行的错觉。
核心概念
- 线程就是独立的执行路径;
- 在程序运行时,即时没有自己创建线程,后台也会有多个线程,如主线程,GC线程;
- main()称之为主线程,为系统的入口,用于执行整个程序;
- 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能认为干预的;
- 对同一份资源操作时,会存在资源抢夺的问题,需要假如并发控制;
- 线程会带来额外的开销,如CPU调度时间,并发控制开销;
- 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致;
三种创建线程的方式:
代码实现的话,不建议使用“继承Thread类”,避免OOP单继承局限性,推荐使用“实现Runnable接口”,因为它能避免单继承局限性,灵活方便,方便同一个对象被多个线程使用。如下:
代码实现 方式一:
1 package com.huolongluo.coindemo.morethread.sub1; 2 3 /** 4 * Created by 火龙裸 on 2019/11/9. 5 * desc : 创建线程方式一:继承Thread类,重写run方法,调用start开启线程。 6 * 7 * 总结:线程开启不一定立即执行,由CPU调度执行 8 * version: 1.0 9 */ 10 public class TestThread1 extends Thread { 11 @Override 12 public void run() { 13 //run方法线程体 14 for (int i = 0; i < 20; i++) { 15 System.out.println("我在看代码--- " + i); 16 } 17 } 18 19 public static void main(String[] args) { 20 //main线程,主线程 21 22 //创建一个线程对象 23 TestThread1 testThread1 = new TestThread1(); 24 //调用start()方法开启线程 25 //注意这个地方,假如调用run方法,虽然会执行run里面的代码,但这是调用,一定会先执行完run里面的代码,最后才打印“今天又是奋斗的一天”,多个线程同时执行,应该使用start方法,这样它们才会交替执行。 26 testThread1.start(); 27 28 for (int i = 0; i < 20; i++) { 29 System.out.println("今天又是奋斗的一天-- " + i); 30 } 31 } 32 }
代码实现 方式二:
1 package com.huolongluo.coindemo.morethread.sub1; 2 3 /** 4 * Created by 火龙裸 on 2019/11/9. 5 * desc : 创建线程方式二:实现Runnable接口,重写run方法,执行线程需要丢入runnable接口的实现类,调用start方法。 6 * version: 1.0 7 */ 8 public class TestThread2 implements Runnable { 9 @Override 10 public void run() { 11 //run方法线程体 12 for (int i = 0; i < 20; i++) { 13 System.out.println("我在看代码--- " + i); 14 } 15 } 16 17 public static void main(String[] args) { 18 //创建runnable接口的实现类对象 19 TestThread2 testThread2 = new TestThread2(); 20 //创建线程对象,通过线程对象来开启我们的线程,代理 21 // Thread thread = new Thread(testThread2); 22 // thread.start(); 23 new Thread(testThread2).start(); 24 for (int i = 0; i < 20; i++) { 25 System.out.println("今天又是奋斗的一天-- " + i); 26 } 27 } 28 }
当需要多个线程操作同一个对象是,那就需要想办法处理“并发问题”,代码示例如下:
1 package com.huolongluo.coindemo.morethread.sub1; 2 3 /** 4 * Created by 火龙裸 on 2019/11/9. 5 * desc : 多线程同时操作同一个对象 6 * 买火车票例子 7 * <p> 8 * 多个线程操作同一个资源对象的情况下,线程不安全,数据紊乱。 9 * version: 1.0 10 */ 11 public class TestThread4 implements Runnable { 12 13 //票数 14 private int ticketNums = 10; 15 16 @Override 17 public void run() { 18 while (true) { 19 if (ticketNums <= 0) { 20 break; 21 } 22 //模拟延时 23 try { 24 Thread.sleep(200); 25 } catch (InterruptedException e) { 26 e.printStackTrace(); 27 } 28 System.out.println(Thread.currentThread().getName() + " 拿到了第:" + ticketNums-- + " 张票"); 29 } 30 } 31 32 public static void main(String[] args) { 33 34 TestThread4 testThread4 = new TestThread4(); 35 36 new Thread(testThread4, "小王").start(); 37 new Thread(testThread4, "老师").start(); 38 new Thread(testThread4, "黄牛党").start(); 39 } 40 }
执行结果:
从执行结果中看到,出票顺序出现异常,第1张票同时被两个人拿到,而且还出现第0张票。这就是需要注意处理的多线程并发问题。