进程与线程

Java基础(第五篇) - 图1
Process与Thread

  • 说起进程就不得不说下程序。程序是指指令和数据的有序集合,本身没有任何运行的含义,是一个静态的概念。
  • 进程则是执行程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位
  • 通常在一个进程中可以包含多个线程,当然一个进程至少有一个线程,不然没有存在的意义。显示CPU调度和执行的单位

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

线程的创建

  • Thread、Runnable、Callable
    • 继承Thread

image.png
image.png

  1. package com.xy.Test;
  2. import org.apache.commons.io.FileUtils;
  3. import java.io.File;
  4. import java.io.IOException;
  5. import java.net.URL;
  6. // 练习Thread、实现多线程
  7. public class Test02 extends Thread {
  8. private String url;
  9. private String name;
  10. public Test02(String url, String name) {
  11. this.url = url;
  12. this.name = name;
  13. }
  14. @Override
  15. public void run() {
  16. WebDownload webDownload = new WebDownload();
  17. webDownload.downloader(url, name);
  18. System.out.println("下载的图片名字为: " + name);
  19. }
  20. public static void main(String[] args) {
  21. Test02 t2 = new Test02("<url>", "apache1");
  22. Test02 t3 = new Test02("<url>", "apache2");
  23. t1.start();
  24. t2.start();
  25. t3.start();
  26. }
  27. }
  28. // 下载类
  29. class WebDownload {
  30. // 下载方法
  31. public void downloader(String url, String name) {
  32. try {
  33. FileUtils.copyURLToFile(new URL(url), new File(name));
  34. } catch (IOException e) {
  35. e.printStackTrace();
  36. }
  37. }
  38. }
  • 实现Runnable接口 ```java package com.xy.Test;

// 创建方法二: 实现Runnable接口,重写run方法,执行线程需要丢入runnable接口实现类 // 调用start方法 public class Test03 implements Runnable{

  1. @Override
  2. public void run() {
  3. for (int i = 0; i < 200; i++) {
  4. System.out.println("run线程" + i);
  5. }
  6. }
  7. public static void main(String[] args) {
  8. // 创建runnable接口的实现类对象
  9. Test03 t1 = new Test03();
  10. // 创建线程对象,通过线程对象来开启我们的线程代理
  11. Thread thread = new Thread(t1);
  12. thread.start();
  13. for (int i = 0; i < 100; i++) {
  14. System.out.println("主线程" + i);
  15. }
  16. }

}

  1. ```java
  2. package com.xy.Test;
  3. // 多个线程同时操作同一个对象
  4. // 买火车票的例子
  5. // 多个线程操作同一个资源的情况下,线程不安全,数据混乱
  6. public class Test04 implements Runnable{
  7. private int ticketNums = 10;
  8. @Override
  9. public void run() {
  10. while (true) {
  11. if (ticketNums <= 0) {
  12. break;
  13. }
  14. try {
  15. Thread.sleep(200);
  16. }catch (InterruptedException e) {
  17. e.printStackTrace();
  18. }
  19. System.out.println(Thread.currentThread().getName() + "拿到了第几张:" + ticketNums-- + "票");
  20. }
  21. }
  22. public static void main(String[] args) {
  23. Test04 test04 = new Test04();
  24. new Thread(test04, "小明").start();
  25. new Thread(test04, "老师").start();
  26. new Thread(test04, "黄牛").start();
  27. }
  28. }
  1. package com.xy.Test;
  2. // 模拟龟兔赛跑
  3. public class Race implements Runnable {
  4. // 胜利者
  5. public static String winner;
  6. @Override
  7. public void run() {
  8. for (int i = 0; i <= 100; i++) {
  9. // 模拟兔子休息
  10. if ((Thread.currentThread().getName().equals("兔")) && (i == 20)) {
  11. try {
  12. Thread.sleep(1);
  13. }catch (InterruptedException e) {
  14. e.printStackTrace();
  15. }
  16. }
  17. // 判断比赛是否结束
  18. boolean flag = gameOver(i);
  19. if(flag) {
  20. break;
  21. }else {
  22. }
  23. System.out.println(Thread.currentThread().getName()+ "--> 跑了" + i +"步");
  24. }
  25. }
  26. // 判断是否完成比赛
  27. private boolean gameOver(int steps) {
  28. // 判断胜利者是否存在
  29. if(winner != null) { // 已经存在胜利者
  30. return true;
  31. } {
  32. if(steps >= 100) {
  33. winner = Thread.currentThread().getName();
  34. System.out.println("winner is" + winner);
  35. return true;
  36. }
  37. }
  38. return false;
  39. }
  40. public static void main(String[] args) {
  41. Race race = new Race();
  42. new Thread(race, "兔").start();
  43. new Thread(race, "龟").start();
  44. }
  45. }
  • 实现Callable接口 ```java package com.xy.Test;

import com.sun.org.apache.xpath.internal.operations.Bool; import org.apache.commons.io.FileUtils;

import java.io.File; import java.io.IOException; import java.net.URL; import java.util.concurrent.*;

// 练习Thread、实现多线程 public class TestCallable implements Callable {

  1. private String url;
  2. private String name;
  3. public TestCallable(String url, String name) {
  4. this.url = url;
  5. this.name = name;
  6. }
  7. @Override
  8. public Boolean call() {
  9. WebDownload1 webDownload1 = new WebDownload1();
  10. webDownload1.downloader(url, name);
  11. System.out.println("下载的图片名字为: " + name);
  12. return true;
  13. }
  14. public static void main(String[] args) throws ExecutionException, InterruptedException {
  15. TestCallable t1 = new TestCallable("https://images.unsplash.com/photo-1650844565749-1f10c8aaa9f9?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=764&q=80", "apache");
  16. TestCallable t2 = new TestCallable("https://images.unsplash.com/photo-1650844565749-1f10c8aaa9f9?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=764&q=80", "apache1");
  17. TestCallable t3 = new TestCallable("https://images.unsplash.com/photo-1650844565749-1f10c8aaa9f9?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=764&q=80", "apache2");
  18. // 创建执行服务
  19. ExecutorService ser = Executors.newFixedThreadPool(3);
  20. // 提交执行
  21. Future<Boolean> r1 = ser.submit(t1);
  22. Future<Boolean> r2 = ser.submit(t2);
  23. Future<Boolean> r3 = ser.submit(t3);
  24. // 获取结果
  25. boolean rs1 = r1.get();
  26. boolean rs2 = r2.get();
  27. boolean rs3 = r3.get();
  28. // 关闭服务
  29. ser.shutdownNow();
  30. }

}

// 下载类 class WebDownload1 { // 下载方法 public void downloader(String url, String name) { try { FileUtils.copyURLToFile(new URL(url), new File(name)); } catch (IOException e) { e.printStackTrace(); } } }

  1. <a name="R3Wck"></a>
  2. ## 代理
  3. <a name="hZZjy"></a>
  4. ### 静态代理
  5. ```java
  6. package com.xy.proxy;
  7. public class StaticProxy {
  8. public static void main(String[] args) {
  9. WeddingCompany weddingCompany = new WeddingCompany(new You());
  10. weddingCompany.HappyMarry();
  11. }
  12. }
  13. interface Marry {
  14. void HappyMarry();
  15. }
  16. // 真实角色
  17. class You implements Marry {
  18. @Override
  19. public void HappyMarry() {
  20. System.out.println("你结婚啦");
  21. }
  22. }
  23. // 代理角色
  24. class WeddingCompany implements Marry {
  25. private Marry target;
  26. public WeddingCompany(Marry target) {
  27. this.target = target;
  28. }
  29. @Override
  30. public void HappyMarry() {
  31. before();
  32. this.target.HappyMarry();
  33. after();
  34. }
  35. private void before() {
  36. System.out.println("结婚之前布置现场");
  37. }
  38. private void after() {
  39. System.out.println("收尾款");
  40. }
  41. }
  • Lamda表达式

    • 避免匿名内部类定义过多
    • 其实质属于函数式编程的概念

      1. new Thread(() -> System.out.println("多线程学习")).start();
    • 理解Functional Interface(函数式接口)是学习Java8 lambda表达式的关键所在

    • 函数式接口的定义:
      • 任何接口,如果包含唯一一个抽象方法,那么它就是一个函数式接口
      • 对于函数式接口,我们可以通过lambda表达式来闯将接口的对象 ```java package com.xy.Lambda;

/**

  • 推导lambda表达式 */

// 定义一个函数式接口(一个接口,只包含一个抽象方法) interface ILike { void lambda(); }

public class TestLambda { private static ILike like; public static void main(String[] args) { like = () -> { System.out.println(“lambda表达式”); }; like.lambda(); } }

  1. <a name="rRbfR"></a>
  2. ## 线程状态
  3. ![](https://cdn.nlark.com/yuque/0/2022/jpeg/13002623/1651295009027-3001f53c-ed1c-46c4-b662-c2cea8966cec.jpeg)
  4. | 方法 | 说明 |
  5. | --- | --- |
  6. | setPriority(int newPriority) | 更改线程的优先级 |
  7. | static void sleep(long millis) | 在指定的毫秒数内让当前正在执行的线程休眠 |
  8. | void join() | 等待该线程终止 |
  9. | static void yield() | 暂停当前正在执行的线程对象,并执行其他线程 |
  10. | void interrupt() | 中断线程,别用这个方式 |
  11. | boolean isAlive() | 测试线程是否处于活动状态 |
  12. ```java
  13. package com.xy.State;
  14. public class Sleep2 {
  15. static public void tenDown() {
  16. int num = 10;
  17. while (true){
  18. if (num <= 0) break;
  19. try {
  20. Thread.sleep(1000);
  21. System.out.println(num--);
  22. } catch (InterruptedException e) {
  23. e.printStackTrace();
  24. }
  25. }
  26. }
  27. public static void main(String[] args) {
  28. tenDown();
  29. }
  30. }
  1. package com.xy.State;
  2. // 测试join方法 、、 插队
  3. public class Join implements Runnable{
  4. @Override
  5. public void run() {
  6. for (int i = 0; i < 100; i++) {
  7. System.out.println("线程VIP来了" + i);
  8. }
  9. }
  10. public static void main(String[] args) {
  11. // 启动线程
  12. Join join = new Join();
  13. Thread thread = new Thread(join);
  14. thread.start();
  15. for (int i = 0; i < 1000; i++) {
  16. if(i == 200) {
  17. try {
  18. thread.join(); // 插队
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. }
  23. System.out.println("main" + i);
  24. }
  25. }
  26. }
  1. package com.xy.State;
  2. // 测试stop
  3. // 1.建议线程正常停止 -------> 利用次数,不建议死循环
  4. // 2.设置一个标志位 -----> 设置一个标志位
  5. // 3.不要使用stop或者destroy等过时或jdk不建议使用的方法
  6. public class Stop implements Runnable {
  7. // 1. 设置一个标志位
  8. private boolean flag = true;
  9. @Override
  10. public void run() {
  11. int i = 0;
  12. while (flag) {
  13. System.out.println("run....Thread" + i++ );
  14. }
  15. }
  16. //2. 设置一个公开的方法停止线程,转换标志位
  17. public void stop() {
  18. this.flag = false;
  19. }
  20. public static void main(String[] args) {
  21. Stop stop = new Stop();
  22. new Thread(stop).start();
  23. for (int i = 0; i < 1000; i++) {
  24. System.out.println("main" + i);
  25. if (i == 900) {
  26. stop.stop();
  27. System.out.println("线程停止");
  28. }
  29. }
  30. }
  31. }
  1. package com.xy.State;
  2. // 线程礼让
  3. public class Yield{
  4. public static void main(String[] args) {
  5. A1 a1 = new A1();
  6. new Thread(a1, "a").start();
  7. new Thread(a1, "b").start();
  8. }
  9. }
  10. class A1 implements Runnable{
  11. @Override
  12. public void run() {
  13. System.out.println(Thread.currentThread().getName() + "线程开始执行!");
  14. Thread.yield();
  15. System.out.println(Thread.currentThread().getName() + "线程停止执行!");
  16. }
  17. }

线程状态

  1. NEW
  • 尚未启动的线程处于此状态
  1. RUNNABLE
  • 在Java虚拟机中执行的线程处于此状态
  1. BLOCKED
  • 被阻塞等待监视器锁定的线程出于此状态
  1. WAITING
  • 正在等待另一个线程执行特定动作的线程处于此状态
  1. TIMED_WAITING
  • 正在等待另一个线程执特定行动作到达指定等待时间的线程, 处于此状态
  1. TERMINATED
  • 已退出的线程出于此状态

    一个线程可以在给定时间点处于一个状态。这些状态是不反应任何操作系统线程状态的虚拟机状态。

  1. package com.xy.State;
  2. // 观察测试线程的状态
  3. public class State {
  4. public static void main(String[] args) {
  5. Thread thread = new Thread(() -> {
  6. for (int i = 0; i < 5; i++) {
  7. try {
  8. Thread.sleep(1000);
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. }
  13. System.out.println("//////////");
  14. });
  15. // 观察状态
  16. Thread.State state = thread.getState();
  17. System.out.println(state); // New
  18. // 观察启动后
  19. thread.start(); // 启动线程
  20. state = thread.getState();
  21. System.out.println(state); // Run
  22. while (state != Thread.State.TERMINATED) {
  23. // 只要线程不终止,就一直输出状态
  24. state = thread.getState();
  25. System.out.println(state);
  26. }
  27. }
  28. }

线程优先级

  • Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调用哪个线程来执行。
  • 线程的优先级用数字表示,范围从1~10
    • Thread.MIN_PROIORITY = 1;
    • Thread.MAX_PROIORITY = 10;
    • Thread.NORM_PROIORITY = 10;
  • 使用以下方式改变或获取优先级
    • getPriority .setPriority(int XXX) ```java package com.xy.Priority;

public class Priority implements Runnable { @Override public void run(){ System.out.println(Thread.currentThread().getName() + “———->” + Thread.currentThread().getPriority() ); } public static void main(String[] args) { System.out.println(Thread.currentThread().getName() + “———->” + Thread.currentThread().getPriority() ); Priority priority = new Priority(); Thread t1 = new Thread(priority, “t1”); Thread t2 = new Thread(priority, “t2”); Thread t3 = new Thread(priority, “t3”); Thread t4 = new Thread(priority, “t4”); Thread t5 = new Thread(priority); Thread t6 = new Thread(priority);

  1. // 设置优先级再启动
  2. t1.start();
  3. t2.setPriority(1);
  4. t2.start();
  5. t3.setPriority(4);
  6. t3.start();
  7. t4.setPriority(Thread.MAX_PRIORITY);
  8. t4.start();
  9. }

}

  1. <a name="pYllE"></a>
  2. ### 守护(daemon)线程
  3. - 线程分为**用户线程**和**守护线程**
  4. - 虚拟机必须确保用户线程执行完毕
  5. - 虚拟机不用等待守护线程执行完毕
  6. - 如,后台记录操作日志,监控内存,垃圾回收等待。。。
  7. ```java
  8. package com.xy.Priority;
  9. // 测试守护线程
  10. public class Daemon {
  11. public static void main(String[] args) {
  12. God god = new God();
  13. You you = new You();
  14. Thread thread = new Thread(god);
  15. thread.setDaemon(true); // 默认为false表示用户线程,正常的线程都是用户线程
  16. thread.start(); // 上帝启动
  17. new Thread(you).start();
  18. }
  19. }
  20. class God implements Runnable {
  21. @Override
  22. public void run() {
  23. while (true) {
  24. System.out.println("上帝保佑你");
  25. }
  26. }
  27. }
  28. class You implements Runnable{
  29. @Override
  30. public void run() {
  31. for (int i = 0; i < 36500; i++) {
  32. System.out.println("你一生都开心的活着");
  33. }
  34. System.out.println("====goodbye!====");
  35. }
  36. }

线程同步

  • 并发: 同一个对象被多个线程同时操作

image.png

  1. package com.xy;
  2. // 线程不安全,有负数
  3. public class UnSafe {
  4. public static void main(String[] args) {
  5. Buy buy = new Buy();
  6. new Thread(buy, "w").start();
  7. new Thread(buy, "n").start();
  8. new Thread(buy, "h").start();
  9. }
  10. }
  11. class Buy implements Runnable {
  12. private int tickNums = 15;
  13. boolean flag = true;
  14. @Override
  15. public void run() {
  16. while (flag) {
  17. try {
  18. buy();
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. }
  23. }
  24. private synchronized void buy() throws InterruptedException {
  25. if(tickNums <= 0) {
  26. flag = false;
  27. return;
  28. }
  29. Thread.sleep(100);
  30. System.out.println(Thread.currentThread().getName()
  31. + "拿到了" + tickNums--
  32. );
  33. }
  34. }

死锁

  • 多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。某一个同步块同事拥有“两个以上对象的锁”时,就可能会发生“死锁”的问题。