线程、进程和多线程

Process与Thread

◆说起进程,就不得不说下程序。程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。

◆而进程则是执行程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位

◆通常在一个进程中可以包含若干个线程,当然-一个进程中至少有一个线程,不然没有存在的意义。线程是CPU调度和执行的的单位。

注意:很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器。如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一一个代码,因为切换的很快,所以就有同时执行的错局。

本章核心概念

◆线程就是独立的执行路径;

◆在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;

◆main()称之为主线程,为系统的入口,用于执行整个程序;

◆在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为的干预的。

◆对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;

◆线程会带来额外的开销,如cpu调度时间, 并发控制开销。

◆每个线程在自己的工作内存交互,内存控制不当会造成数据不一致;

线程创建

三种继承方式

Thread class 继承Thread类 (重点)

Runnable 接口 实现Runnable接口(重点)

Callable接口 实现Callable接口(了解)

用户线程 + 守护线程

Thread class

  • 自定义线程类继承Thread类;
  • 重写run()方法,编写线程执行体;
  • 创建线程对象,调用start()方法启用线程;
  1. package Dome;
  2. //创建线程方式一: 继承Thread类,重写run()方法,调用start()方法开启线程
  3. /**
  4. * @author dafran
  5. * @version 1.0
  6. * @date 2020-05-21 16:50
  7. */
  8. //线程开启也不一定立即执行,线程的调度是CPU调度执行,交叉执行的。
  9. public class TestThread01 extends Thread{
  10. @Override
  11. public void run() {
  12. //run方法线程体
  13. for (int i = 0; i < 20; i++) {
  14. System.out.println("run方法"+i);
  15. }
  16. }
  17. public static void main(String[] args) {
  18. //main方法,主线程
  19. //创建一个线程对象
  20. TestThread01 testThread01 = new TestThread01();
  21. //调用start()方法开启线程
  22. testThread01.start();
  23. for (int i = 0; i < 20; i++) {
  24. System.out.println("main方法"+i);
  25. }
  26. }
  27. }

注意:线程开启也不一定立即执行,线程的调度是CPU调度执行,交叉执行的。

实现Runnable

  • 定义MyRunnable类实现Runnable接口
  • 实现run()方法,编写线程执行体
  • 创建线程对象,调用start()方法启动线程

推荐使用Runnable对象,因为Java单继承的局限性

  1. package Dome;
  2. //创建线程方式二:实现runnable接口,重写run()方法,执行线程需要丢入runnable接口实现类,调用start()方法.
  3. /**
  4. * @author dafran
  5. * @version 1.0
  6. * @date 2020-05-21 19:52
  7. */
  8. public class TestThread02 implements Runnable {
  9. @Override
  10. public void run() {
  11. //run方法线程体
  12. for (int i = 0; i < 20; i++) {
  13. System.out.println("run方法"+i);
  14. }
  15. }
  16. public static void main(String[] args) {
  17. //创建runnable接口实现类对象
  18. TestThread02 testThread02 = new TestThread02();
  19. //创建线程对象,通过线程对象来开启我们的线程,代理;
  20. /*Thread thread = new Thread(testThread02);
  21. thread.start();*/
  22. new Thread(testThread02).start();
  23. for (int i = 0; i < 20; i++) {
  24. System.out.println("main方法"+i);
  25. }
  26. }
  27. }

小结:

◆继承Thread类

  1. ◆子类继承Thread类具备多线程能力
  2. ◆启动线程:子类对象. start()
  3. ◆不建议使用:避免OOP单继承局限性

◆实现Runnable接口

  1. ◆实现接口Runnable具有多线程能力
  2. ◆启动线程:传入目标对象+ Thread对象.start()
  3. ◆推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
  1. package Dome;
  2. //多个线程同时操作一个对象
  3. //火车票为例
  4. /**
  5. * @author dafran
  6. * @version 1.0
  7. * @date 2020-05-22 17:27
  8. */
  9. public class TestThread03 implements Runnable {
  10. //票数
  11. private int ticketNums = 10;
  12. @Override
  13. public void run() {
  14. while (true) {
  15. if (ticketNums<=0){
  16. break;
  17. }
  18. System.out.println(Thread.currentThread().getName() + "-->拿到了第" + ticketNums-- + "票");
  19. }
  20. }
  21. public static void main(String[] args) {
  22. TestThread03 ticket = new TestThread03();
  23. new Thread(ticket,"dafran").start();
  24. new Thread(ticket,"cola").start();
  25. new Thread(ticket,"6b92d6").start();
  26. }
  27. }
  1. package Dome;
  2. // 模拟龟兔赛跑
  3. import static java.lang.Thread.sleep;
  4. /**
  5. * @author dafran
  6. * @version 1.0
  7. * @date 2020-05-22 21:38
  8. */
  9. public class Race implements Runnable {
  10. //胜利者
  11. private static String winner;
  12. @Override
  13. public void run() {
  14. for (int i = 0; i <= 100; i++) {
  15. //模拟兔子睡觉
  16. if(Thread.currentThread().getName().equals("兔子") && i%10 == 0){
  17. try {
  18. sleep(10);
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. }
  23. //判断比赛是否结束
  24. boolean flag = gameOver(i);
  25. //如果比赛结束了,就停止程序
  26. if (flag){
  27. break;
  28. }
  29. System.out.println(Thread.currentThread().getName() + "-->跑了" + i + "步");
  30. }
  31. }
  32. //判断是否完成比赛
  33. private boolean gameOver(int steps) {
  34. //判断是否有胜利者
  35. if (winner != null) {
  36. return true;
  37. }{
  38. if (steps >= 100) {
  39. winner =Thread.currentThread().getName();
  40. System.out.println("winner is" + winner);
  41. return true;
  42. }
  43. }
  44. return false;
  45. }
  46. public static void main(String[] args) {
  47. Race race = new Race();
  48. new Thread(race,"兔子").start();
  49. new Thread(race,"乌龟").start();
  50. }
  51. }

实现Callable接口(了解即可)

  • 实现Callable接口, 需要返回值类型
  • 重写call方法, 需要抛出异常
  • 创建目标对象
  • 创建执行服务: ExecutorService ser = Executors.newFixedThreadPool(1);5.提交执行:Futureresult1 = ser.submit(t1);
  • 获取结果: boolean r1 = result1.get()
  • 关闭服务: ser.shutdownNow();
  1. package Dome;
  2. import java.util.concurrent.*;
  3. //线程创建方式三:实现callable接口
  4. /*
  5. callable的好处
  6. 1.可以定义返回值
  7. 2.可以抛出异常
  8. */
  9. /**
  10. * @author dafran
  11. * @version 1.0
  12. * @date 2020-05-23 18:45
  13. */
  14. //实现Callable接口, 需要返回值类型
  15. public class TestCallable implements Callable<Boolean> {
  16. //票数
  17. private int ticketNums = 10;
  18. //重写call方法, 需要抛出异常
  19. @Override
  20. public Boolean call() throws Exception {
  21. while (true) {
  22. if (ticketNums<=0){
  23. break;
  24. }
  25. System.out.println(TestCallable.this.ticketNums + "-->拿到了第" + ticketNums-- + "票");
  26. }
  27. return true;
  28. }
  29. public static void main(String[] args) throws ExecutionException, InterruptedException {
  30. //创建目标对象
  31. TestCallable ticket1 = new TestCallable();
  32. TestCallable ticket2 = new TestCallable();
  33. TestCallable ticket3 = new TestCallable();
  34. //创建执行服务
  35. ExecutorService ser = Executors.newFixedThreadPool(3);
  36. //提交执行
  37. Future<Boolean> r1 = ser.submit(ticket1);
  38. Future<Boolean> r2 = ser.submit(ticket2);
  39. Future<Boolean> r3 = ser.submit(ticket3);
  40. System.out.println(r1);
  41. System.out.println(r2);
  42. System.out.println(r3);
  43. //获取结果
  44. boolean rs1 = r1.get();
  45. boolean rs2 = r2.get();
  46. boolean rs3 = r3.get();
  47. //关闭服务
  48. ser.shutdownNow();
  49. }
  50. }

静态代理

  1. package Dome;
  2. /*静态代理模式总结:
  3. 真实对象和代理对象都要实现同一个接口
  4. 代理对象要代理真实角色*/
  5. /*好处:
  6. 代理对象可以做很多真实对象做不了的事情
  7. 真实对象专注做自己的事仿*/
  8. /**
  9. * @author dafran
  10. * @version 1.0
  11. * @date 2020-05-23 20:07
  12. */
  13. public class StaticProxy {
  14. public static void main(String[] args) {
  15. //类比 Thread 利用了 静态代理
  16. /**new Thread(()-> System.out.println("loveYou")).start();
  17. new WeddingCompany(new You()).HappyMarry(); */
  18. WeddingCompany weddingCompany = new WeddingCompany(new You());
  19. weddingCompany.HappyMarry();
  20. }
  21. }
  22. interface Marry {
  23. void HappyMarry();
  24. }
  25. class You implements Marry {
  26. @Override
  27. public void HappyMarry() {
  28. System.out.println("淦,老子结婚了");
  29. }
  30. }
  31. class WeddingCompany implements Marry {
  32. private Marry target;
  33. public WeddingCompany(Marry target) {
  34. this.target = target;
  35. }
  36. @Override
  37. public void HappyMarry() {
  38. before();
  39. this.target.HappyMarry();
  40. after();
  41. }
  42. private void before() {
  43. System.out.println("布置婚礼现场");
  44. }
  45. private void after() {
  46. System.out.println("收拾现场,收钱");
  47. }
  48. }

Lamda表达式

  • λ希腊字母表第十一个字母,英文Lambda
  • 避免匿名内部类定义过多
  • 其实质属于函数式编程的概念

(params) -> experssin [表达式] (params) -> statement[语句] (params) -> {statement}

  1. a -> System.out.println("This is Lambda -->"+a);
  2. new Thread (() -> System.out.println("多线程的学习")) .start();
  • 为什么要使用lambda表达式
    • 避免匿名内部类定义过多。
    • 去掉了-堆没有意义的代码,只留下核心的逻辑。
    • 可以让你的代码看起来很简洁。
  • 也许你会说,我看了Lambda表达式,不但不觉得简洁,反而觉得更乱,看不懂了。那是因为我们还没有习惯, 用的多了,看习惯了,就好了。

Lamda表达式 :

  • 理解Functional lnterface (函数式接口) 是学习Java8 lambda表达式的关键所在。
  • 函数式接口的定义:

    • 任何借口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口。
      1. public interface Runnable {
      2. public abstract void run();
      3. }
  • 对于函数式接口,我们可以通过lambda表达式来创建该接口的对象。

  1. package Dome;
  2. /*
  3. 推导lambda表达式
  4. */
  5. /**
  6. * @author dafran
  7. * @version 1.0
  8. * @date 2020-05-24 17:18
  9. */
  10. public class TestLambda01 {
  11. //3.静态内部类
  12. static class Like2 implements ILike {
  13. @Override
  14. public void lambda() {
  15. System.out.println("This is Lambda2");
  16. }
  17. }
  18. public static void main(String[] args) {
  19. ILike like = new Like();
  20. like.lambda();
  21. like = new Like2();
  22. like.lambda();
  23. //4.局部内部类
  24. class Like3 implements ILike {
  25. @Override
  26. public void lambda() {
  27. System.out.println("This is Lambda3");
  28. }
  29. }
  30. like = new Like3();
  31. like.lambda();
  32. //5.匿名内部类,没有类的名称,必须借助接口或者父类
  33. like = new ILike() {
  34. @Override
  35. public void lambda() {
  36. System.out.println("This is Lambda4");
  37. }
  38. };
  39. like.lambda();
  40. //6.用Lambda简化
  41. like = ()->{
  42. System.out.println("This is Lambda5");
  43. };
  44. like.lambda();
  45. }
  46. }
  47. //1.定义一个函数式接口
  48. interface ILike{
  49. void lambda();
  50. }
  51. //2.实现类
  52. class Like implements ILike {
  53. @Override
  54. public void lambda() {
  55. System.out.println("This is Lambda");
  56. }
  57. }
  1. package Dome;
  2. /**
  3. * @author dafran
  4. * @version 1.0
  5. * @date 2020-05-24 17:47
  6. */
  7. public class TestLambda02{
  8. public static void main(String[] args) {
  9. ILove love = null;
  10. //1.Lambda简化
  11. love = (int a) ->{
  12. System.out.println("This is Love->" + a);
  13. };
  14. //2.参数简化
  15. love = (a) ->{
  16. System.out.println("This is Love->" + a);
  17. };
  18. //3.简化括号
  19. love = a -> {
  20. System.out.println("This is Love->" + a);
  21. };
  22. //4.简化花括号
  23. love = a -> System.out.println("This is Love->" + a);
  24. // 总结:
  25. // lambda表达式只能有一行代码的情况下才能简化成为行,如果有多行, 那么就用代码块包
  26. // 前提是接口为函数式接口
  27. // 多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号
  28. love.love(520);
  29. }
  30. }
  31. interface ILove{
  32. void love(int a);
  33. }

线程的状态

方法 说明
setPriority(int newPriority) 更改线程的优先级
static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠
void join() 等待该线程终止
static void yield() 暂停当前正在执行的线程对象,并执行其他线程
void interrupt() 中断线程,别用这个方式
boolean isAlive() 测试线程是否处于活动状态

停止线程

  • 不推荐使用JDK提供的 stop()、destroy()。[已废弃]
  • 推荐线程自己停下来
  • 建议使用一个标志位进行终止变量,当flag = false, 则终止线程运行。
  1. package Dome;
  2. /**
  3. * @author dafran
  4. * @version 1.0
  5. * @date 2020-05-26 15:38
  6. */
  7. public class TestStop implements Runnable {
  8. //1.设置标识位
  9. private boolean flag = true;
  10. @Override
  11. public void run() {
  12. int i = 0;
  13. while (flag){
  14. System.out.println("run...Thread" + i++);
  15. }
  16. }
  17. //2.设置一个公开的方法停止线程,转换标志位
  18. public void stop(){
  19. this.flag = false;
  20. }
  21. public static void main(String[] args){
  22. TestStop testStop = new TestStop();
  23. new Thread(testStop).start();
  24. for (int i = 0; i < 10000; i++) {
  25. System.out.println("main"+i);
  26. if (i==9000){
  27. //调用stop方法
  28. testStop.stop();
  29. System.out.println("stop");
  30. }
  31. }
  32. }
  33. }

线程休眠

  • sleep(时间) 指定当前线程阻塞的毫秒数;
  • sleep存在异常InterruptedException;
  • sleep时间达到后线程进入就绪队列;
  • sleep可以模拟网络延时,倒计时等;
  • 每一个对象都有一个锁,sleep不会释放锁;
  1. package Dome;
  2. /**
  3. * @author dafran
  4. * @version 1.0
  5. * @date 2020-05-26 16:46
  6. */
  7. public class TestSleep implements Runnable {
  8. //票数
  9. private int ticketNums = 10;
  10. @Override
  11. public void run() {
  12. while (true) {
  13. if (ticketNums<=0){
  14. break;
  15. }
  16. //模拟网络延时:方法问题的发生性。
  17. try {
  18. Thread.sleep(100);
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. System.out.println(Thread.currentThread().getName() + "-->拿到了第" + ticketNums-- + "票");
  23. }
  24. }
  25. public static void main(String[] args) {
  26. TestSleep ticket = new TestSleep();
  27. new Thread(ticket,"dafran").start();
  28. new Thread(ticket,"cola").start();
  29. new Thread(ticket,"6b92d6").start();
  30. }
  31. }
  1. package Dome;
  2. import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;
  3. import java.text.SimpleDateFormat;
  4. import java.util.Date;
  5. /**
  6. * @author dafran
  7. * @version 1.0
  8. * @date 2020-05-26 16:53
  9. */
  10. public class TestSleep02 {
  11. public static void main(String[] args) {
  12. //打印系统当前时间
  13. Date startTime = new Date(System.currentTimeMillis()); //获取当前时间
  14. while (true) {
  15. try {
  16. Thread.sleep(1000);
  17. System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
  18. startTime = new Date(System.currentTimeMillis()); //更新当前时间
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. }
  23. /*try {
  24. tenDown();
  25. } catch (InterruptedException e) {
  26. e.printStackTrace();
  27. }*/
  28. }
  29. public static void tenDown() throws InterruptedException {
  30. int num = 10; //倒计时10秒
  31. while (true) {
  32. Thread.sleep(1000);
  33. System.out.println(num--);
  34. if (num <= 0) {
  35. break;
  36. }
  37. }
  38. }
  39. }

线程礼让

  • 礼让线程,让当前正在执行的线程暂停,但不阻塞;
  • 将线程从运行状态转为就绪状态;
  • 让CPU重新调度,礼让不一定成功,看CPU心情
  1. package Dome;
  2. /**
  3. * @author dafran
  4. * @version 1.0
  5. * @date 2020-05-26 17:28
  6. */
  7. public class TestYileld {
  8. public static void main(String[] args) {
  9. MyYield myYield = new MyYield();
  10. new Thread(myYield,"a").start();
  11. new Thread(myYield,"b").start();
  12. }
  13. }
  14. class MyYield implements Runnable {
  15. @Override
  16. public void run() {
  17. System.out.println(Thread.currentThread().getName()+"线程开始执行");
  18. Thread.yield(); //礼让
  19. System.out.println(Thread.currentThread().getName()+"线程停止执行");
  20. }
  21. }

合并线程

  • Join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞。
  • 类似插队(VIP)
  1. package Dome;
  2. /**
  3. * @author dafran
  4. * @version 1.0
  5. * @date 2020-05-26 20:19
  6. */
  7. public class TestJoin implements Runnable {
  8. @Override
  9. public void run() {
  10. for (int i = 0; i < 100; i++) {
  11. System.out.println("VIP高调路过" + i);
  12. }
  13. }
  14. public static void main(String[] args) throws InterruptedException {
  15. //启动我们的线程
  16. TestJoin testJoin = new TestJoin();
  17. Thread thread = new Thread(testJoin);
  18. thread.start();
  19. //主线程
  20. for (int i = 0; i < 500; i++) {
  21. if (i == 200){
  22. thread.join(); //插队
  23. }
  24. System.out.println("main"+i);
  25. }
  26. }
  27. }

生产者消费者

  1. package Dome;
  2. import java.util.Random;
  3. import java.util.concurrent.ExecutorService;
  4. import java.util.concurrent.Executors;
  5. import java.util.concurrent.LinkedBlockingQueue;
  6. import java.util.concurrent.TimeUnit;
  7. /**
  8. * @author dafran
  9. * @version 1.0
  10. * @date 2020-05-25 19:35
  11. */
  12. public class ProducerConsumer{
  13. public static void main(String[] args) {
  14. // 任务调度器
  15. ExecutorService exec = Executors.newFixedThreadPool(10);
  16. // 仓库
  17. final LinkedBlockingQueue<Integer> buffer = new LinkedBlockingQueue<>(5);
  18. for (int i = 0; i < 2; i++) {
  19. // 创建生产者
  20. Producer p = new Producer(buffer);
  21. // 领到把生产者拉到车间,被迫没日没夜的干活
  22. exec.execute(p);
  23. // 消费者出生了
  24. Consumer c = new Consumer(buffer);
  25. // 消费者一生都在消费
  26. exec.execute(c);
  27. }
  28. exec.execute(new Runnable() {
  29. @Override
  30. public void run() {
  31. while (true) {
  32. // 定时看一下仓库的空间
  33. System.out.println("buffer :" + buffer.size());
  34. try {
  35. TimeUnit.SECONDS.sleep(5);
  36. } catch (InterruptedException e) {
  37. e.printStackTrace();
  38. }
  39. }
  40. }
  41. });
  42. }
  43. }
  44. //消费者
  45. class Consumer implements Runnable {
  46. LinkedBlockingQueue<Integer> buffer;
  47. //注册仓库
  48. Consumer(LinkedBlockingQueue<Integer> buffer) {
  49. this.buffer = buffer;
  50. }
  51. /**
  52. * 从仓库中的取出产品消费,当仓库里面没有产品时,会一直等下去
  53. * @return
  54. * @throws InterruptedException
  55. */
  56. public Integer consume() throws InterruptedException {
  57. Integer e = buffer.take();
  58. return e;
  59. }
  60. @Override
  61. public void run() {
  62. Random random = new Random(3);
  63. try {
  64. while (true) {//一生都要吃
  65. Integer product = consume();
  66. System.out.println(this + " \tConsume:\t " + product);
  67. TimeUnit.MILLISECONDS.sleep(random.nextInt(2000));//吃了也要睡会
  68. }
  69. } catch (InterruptedException e) {
  70. e.printStackTrace();
  71. }
  72. }
  73. }
  74. //生产者
  75. class Producer implements Runnable{
  76. LinkedBlockingQueue<Integer> buffer;
  77. //构造生产者,注册仓库
  78. Producer(LinkedBlockingQueue<Integer> buffer) {
  79. this.buffer = buffer;
  80. }
  81. /**
  82. * 生产一个产品,当仓库已经满时,等待仓库有空地再放入仓库
  83. * @param e
  84. * @throws InterruptedException
  85. */
  86. public void produce (Integer e) throws InterruptedException {
  87. buffer.put(e);
  88. }
  89. @Override
  90. public void run() {
  91. Random random = new Random(3);
  92. try {
  93. while (true){
  94. Integer product = random.nextInt();
  95. System.out.println(this + " Priduct: " + product);
  96. produce(product);
  97. TimeUnit.MILLISECONDS.sleep(random.nextInt(500));//短暂的休息
  98. }
  99. } catch (InterruptedException e) {
  100. e.printStackTrace();
  101. }
  102. }
  103. }