• Java开发:多线程编程


    本章篇幅主要记录多线程编程相关的知识,如有纰漏,望指出。

    话不多说,正式开启多线程之旅...

    目录

    一、多线程使用方式

    A、Thread

    B、Runnable(推荐)

    C、Callable

    二、线程的五个状态

    三、线程停止

    四、线程休眠sleep

    五、线程礼让yield

    六、线程插队join

    七、线程优先级设置

    八、守护线程deamon

    九、线程同步

    A、数据并发解决办法-synchronized

    B、线程安全集合-CopyOnWriteArrayList

    C、可重用锁-ReentrantLock

    十、死锁

    十一、线程池


    一、多线程使用方式

    A、Thread

    使用方法:继承Thread类,不建议使用,避免OOP单继承局限性

    示例:多线程下载网络图片

    首先,百度搜索导入commonsio.jar,并将jar包导入项目中。

    1. /**
    2. * 封装下载器
    3. */
    4. public class FileDownload {
    5. public void download(String url, String fileName) {
    6. try {
    7. FileUtils.copyURLToFile(new URL(url), new File(fileName));
    8. } catch (IOException e) {
    9. System.out.println("IO异常FileDownload");
    10. }
    11. }
    12. }
    13. /**
    14. * 异步下载网络图片
    15. */
    16. public class LessonThread1 extends Thread {
    17. private String url;
    18. private String fileName;
    19. public LessonThread1(String url, String fileName) {
    20. this.url = url;
    21. this.fileName = fileName;
    22. }
    23. @Override
    24. public void run() {
    25. FileDownload fileDownload = new FileDownload();
    26. fileDownload.download(url, fileName);
    27. System.out.println(fileName + "图片下载完成");
    28. }
    29. }
    30. /**
    31. * 执行下载
    32. */
    33. public class Application {
    34. public static void main(String[] args) {
    35. new LessonThread1("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png", "D:\\code\\JavaProject\\Files\\1.jpg").start();
    36. }
    37. }

    B、Runnable(推荐)

    使用方法:实现Runnable接口,推荐使用,避免单继承局限性,灵活方便,方便同一个对象被多个线程使用。

    示例:龟兔赛跑

    1. public class Race implements Runnable {
    2. private String winner;
    3. @Override
    4. public void run() {
    5. for (int i = 0; i <= 100; i++) {
    6. if (Thread.currentThread().getName().contains("兔子") && i % 10 == 0) {
    7. try {
    8. Thread.sleep(1);
    9. } catch (InterruptedException e) {
    10. throw new RuntimeException(e);
    11. }
    12. }
    13. if (gameOver(i)) {
    14. break;
    15. }
    16. System.out.println(Thread.currentThread().getName() + "--->跑了" + i + "步");
    17. }
    18. }
    19. private boolean gameOver(int i) {
    20. if (winner != null) {
    21. return true;
    22. }
    23. if (i >= 100) {
    24. winner = Thread.currentThread().getName();
    25. System.out.println("winner is " + winner);
    26. return true;
    27. }
    28. return false;
    29. }
    30. }
    31. public class Application {
    32. public static void main(String[] args) {
    33. Race race = new Race();
    34. Thread t1 = new Thread(race, "小兔子");
    35. Thread t2 = new Thread(race, "乌龟");
    36. t1.start();
    37. t2.start();
    38. }
    39. }

    C、Callable

    使用方法略

    二、线程的五个状态

     

    三、线程停止

    1、建议线程正常停止--->利用次数,不建议死循环;

    2、建议使用标志位进行终止变量 ,当flag= false时终止线程运行--->设置一个标志位;

    3、建议使用Thread.interrupt()标志位,原理同方法2;

    4、不要使用stop或者destroy等过时API;

    1. public class StopThread implements Runnable {
    2. private boolean flag = true;
    3. @Override
    4. public void run() {
    5. int i = 0;
    6. while (flag) {
    7. System.out.println("run thread num --->" + i++);
    8. }
    9. }
    10. private void stop() {
    11. flag = false;
    12. }
    13. public static void main(String[] args) {
    14. StopThread stopThread = new StopThread();
    15. new Thread(stopThread).start();
    16. for (int i = 0; i < 1000; i++) {
    17. System.out.println("main thread num --->" + i);
    18. if (i == 900) {
    19. stopThread.stop();
    20. System.out.println("StopThread线程该停止了");
    21. }
    22. }
    23. }
    24. }

    四、线程休眠sleep

    1. public class SleepThread {
    2. public static void main(String[] args) {
    3. try {
    4. sleep();
    5. } catch (InterruptedException e) {
    6. throw new RuntimeException(e);
    7. }
    8. }
    9. private static void sleep() throws InterruptedException {
    10. int i = 10;
    11. while (true) {
    12. System.out.println(i--);
    13. Thread.sleep(1_000);
    14. if (i <= 0) {
    15. break;
    16. }
    17. }
    18. }

    五、线程礼让yield

    暂停自己的线程,和其他的线程重新竞争cpu资源

    六、线程插队join

    插队的线程优先执行,且插队线程执行完成后,其他线程才能继续执行。

    七、线程优先级设置

    Thread.setPriority(),范围从低到高是1-10

    优先级越高,大概率会优先执行。
    线程默认优先值为5

    八、守护线程deamon

    1、线程分为用户线程和守护线程;

    2、虚拟机必须确保用户线程执行完毕,但不用等待守护线程执行完毕;

    1. public class DaemonThread {
    2. public static void main(String[] args) {
    3. You you = new You();
    4. God god = new God();
    5. Thread threadGod = new Thread(god, "上帝");
    6. threadGod.setDaemon(true);//设置守护线程
    7. threadGod.start();
    8. new Thread(you, "你").start();
    9. }
    10. }
    11. class You implements Runnable {
    12. @Override
    13. public void run() {
    14. for (int i = 0; i < 30_000; i++) {
    15. System.out.println("开心的活着");
    16. }
    17. }
    18. }
    19. class God implements Runnable {
    20. @Override
    21. public void run() {
    22. while (true) {
    23. System.out.println("上帝保佑着你");
    24. }
    25. }
    26. }

    九、线程同步

    多个线程操作同一个资源,就会造成数据混乱。

    A、数据并发解决办法-synchronized

    锁的对象:变化的量(并发时共同修改的那个变量)
    有两种用法:synchronized方法和synchronized块
    缺陷:如果方法加锁会大大影响效率

    1. public class UnsafeList {
    2. public static void main(String[] args) {
    3. List<String> strings = new ArrayList<>();
    4. for (int i = 0; i < 10000; i++) {
    5. new Thread() {
    6. @Override
    7. public void run() {
    8. //锁代码块,锁的对象是多线程要修改的公共变量
    9. synchronized (strings) {
    10. strings.add(Thread.currentThread().getName());
    11. }
    12. }
    13. }.start();
    14. }
    15. try {
    16. Thread.sleep(2000);
    17. } catch (InterruptedException e) {
    18. throw new RuntimeException(e);
    19. }
    20. System.out.println(strings.size());
    21. }
    22. }

    B、线程安全集合-CopyOnWriteArrayList

    1. public class TestJUC {
    2. public static void main(String[] args) {
    3. //位于java.util.concurrent包
    4. CopyOnWriteArrayList<String> strings = new CopyOnWriteArrayList<>();
    5. for (int i = 0; i < 10000; i++) {
    6. new Thread(() -> {
    7. strings.add(Thread.currentThread().getName());
    8. }).start();
    9. }
    10. try {
    11. Thread.sleep(2000);
    12. } catch (InterruptedException e) {
    13. throw new RuntimeException(e);
    14. }
    15. System.out.println(strings.size());
    16. }
    17. }

    C、可重用锁-ReentrantLock

    Lock只能锁代码块,synchronized可以锁代码块和方法
     

    1. try{
    2. lock.lock();
    3. //代码逻辑
    4. }finally{
    5. lock.unLock();
    6. }

    十、死锁

    定义:多个线程互相持有对方需要的资源,然后行程僵持的状态。

    产生的条件:

    1、一个资源每次只能被一个线程使用;

    2、一个线程因请求资源而阻塞时,对已获得的资源保持不放;

    3、线程已获得的资源,在未使用之前,不能强行剥夺;

    4、若干线程之间形成一种头尾相接的循环等待资源关系;

    1. public class DeadLock {
    2. public static void main(String[] args) {
    3. new Makeup(0, "灰姑凉").start();
    4. new Makeup(1, "白雪公主").start();
    5. }
    6. }
    7. class Lipstick {
    8. //口红
    9. }
    10. class Mirror {
    11. //镜子
    12. }
    13. class Makeup extends Thread {
    14. static Lipstick lipstick = new Lipstick();
    15. static Mirror mirror = new Mirror();
    16. private int choice;
    17. private String girlName;
    18. Makeup(int choice, String girlName) {
    19. this.choice = choice;
    20. this.girlName = girlName;
    21. }
    22. @Override
    23. public void run() {
    24. makeup();
    25. }
    26. private void makeup() {
    27. if (choice == 0) {
    28. synchronized (lipstick) {
    29. System.out.println(girlName + "持有口红锁");
    30. try {
    31. Thread.sleep(1000);
    32. } catch (InterruptedException e) {
    33. throw new RuntimeException(e);
    34. }
    35. synchronized (mirror) {
    36. System.out.println(girlName + "持有镜子");
    37. }
    38. }
    39. } else if (choice == 1) {
    40. synchronized (mirror) {
    41. System.out.println(girlName + "持有镜子");
    42. try {
    43. Thread.sleep(1000);
    44. } catch (InterruptedException e) {
    45. throw new RuntimeException(e);
    46. }
    47. synchronized (lipstick) {
    48. System.out.println(girlName + "持有口红锁");
    49. }
    50. }
    51. }
    52. }
    53. }

    十一、线程池

    ExecutorService和Executors
    ExecutorService:真正的线程池接口
    Executors:工具类、线程池的工厂类

    end

  • 相关阅读:
    【第四阶段】kotlin语言的定义类和field关键字学习
    上周热点回顾(9.12-9.18)
    Android Jetpack Compose之确定重组范围并优化重组
    Linux共享内存与子进程继承
    强化学习环境 - robogym - 学习 - 2
    人工智能轨道交通行业周刊-第9期(2022.8.8-8.14)
    软件设计模式系列之四——简单工厂模式
    阿里云ModelScope 是一个“模型即服务”(MaaS)平台
    case when 结合sum()函数使用
    小程序用户体系全攻略:openid和code的神秘连接,快速实现登录功能!告别繁琐的账号密码验证!
  • 原文地址:https://blog.csdn.net/android157/article/details/127934855