• 【Java高级编程】Java多线程学习笔记


    Java 多线程

    1.多线程创建

    方法1:通过 继承 thread 类

    [子线程代码] MyThread.java

    1. package example01_thread;
    2. public class MyThread extends Thread {
    3. @Override
    4. public void run() {
    5. // super.run();
    6. for (int i = 0; i <= 1000; i++) {
    7. System.out.println("===> 子线程 " + i);
    8. }
    9. }
    10. }

    [主线程代码] Test.java

    1. package example01_thread;
    2. public class Test {
    3. public static void main(String[] args) {
    4. MyThread myThread = new MyThread();
    5. //myThread.run(); //方法调用,单线程。
    6. myThread.start(); // 启动线程,多线程。 这个.statr() 继承自Thread类
    7. for (int i = 0; i <= 1000; i++) {
    8. System.out.println("===> 主线程 " + i);
    9. }
    10. }
    11. }

    [运行结果]

    img.png

    方法2:通过 实现 Runnable 接口

    [子线程代码] MyRunnable.java

    1. package example02_runnable;
    2. public class MyRunnable implements Runnable {
    3. @Override
    4. public void run() {
    5. for (int i = 0; i <= 1000; i++) {
    6. System.out.println("===> 子线程 " + i);
    7. }
    8. }
    9. }

    [主线程代码] Test2.java

    1. package example02_runnable;
    2. public class Test2 {
    3. public static void main(String[] args) {
    4. Runnable runnable = new MyRunnable();
    5. Thread thread = new Thread(runnable);
    6. thread.start();
    7. for (int i = 0; i <= 1000; i++) {
    8. System.out.println("===> 主线程 " + i);
    9. }
    10. }
    11. }

    [运行结果]

    img.png

    2.线程中的相关方法

    (1)设置优先级 setPrlorty()

    优先级可以用从1到10的范围指定。10表示最高优先级(Thread.MAX_PRIORITY),1表示最低优先级(Thread.MIN_PRIORITY),5是普通优先级(Thread.NORM_PRIORITY,默认)。

    优先级不能超出1-10的取值范围,否则抛出IllegalArgumentException

    效果受操作系统影响,可能存在无效情况,因此不要有业务逻辑依赖于线程优先级,结果会无法预期

    • Thread.setPriority()用来设定线程的优先级
    • 在线程 start 方法被调用之前,线程的优先级应该被设定
    • 线程的优先级不能超过所属线程组的优先级,即可以通过控制线程组的优先级,来控制线程组下线程的最大优先级
    • 线程的优先级未设定时,默认所属的线程组的优先级
    • 未指定线程优先级时,所有线程都携带普通优先级
    • 优先级最高的线程在执行时被给予优先,最终由 CPU 调度程序决定哪一个线程被执行
    • 与线程池中等待运行机会的线程相比,当前正在运行的线程可能总是拥有更高的优先级
    • 高优先级线程不一定先于低优先级的线程运行。

    [子线程代码] MyThread3.java

    1. package example03_priority;
    2. public class MyThread3 extends Thread{
    3. public MyThread3(String name){
    4. super.setName(name); //设置线程名字
    5. }
    6. @Override
    7. public void run() {
    8. //super.run();
    9. for(int i = 0;i<=1000;i++){
    10. System.out.println("===> 子线程 " +super.getName() + " "+ i);
    11. }
    12. }
    13. }

    [主线程代码] Test3.java (未设置线程优先级)

    1. package example03_priority;
    2. public class Test3 {
    3. public static void main(String[] args) {
    4. MyThread3 mt1 = new MyThread3("A");
    5. MyThread3 mt2 = new MyThread3("B");
    6. mt1.start();
    7. mt2.start();
    8. }
    9. }

    [运行结果]

    img.png

    [主线程代码] Test3.java (设置线程优先级)

    1. package example03_priority;
    2. public class Test3 {
    3. public static void main(String[] args) {
    4. MyThread3 mt1 = new MyThread3("A");
    5. MyThread3 mt2 = new MyThread3("B");
    6. mt1.setPriority(1);
    7. mt2.setPriority(10);
    8. mt1.start();
    9. mt2.start();
    10. }
    11. }

    [运行结果]

    img.png

    img.png

    (2)设置睡眠 sleep()

    sleep,每隔x时间 去执行一个y操作。参数单位:毫秒

    1. package example04_sleep;
    2. import java.text.SimpleDateFormat;
    3. import java.util.Date;
    4. public class MyThread4 extends Thread{
    5. @Override
    6. public void run() {
    7. //让子系统不停的显示系统当前时间
    8. while(true){
    9. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    10. Date d = new Date();
    11. System.out.println(sdf.format(d));
    12. try {
    13. Thread.sleep(1000);
    14. } catch (InterruptedException e) {
    15. e.printStackTrace();
    16. }
    17. }
    18. }
    19. public static void main(String[] args) {
    20. MyThread4 mt = new MyThread4();
    21. mt.start();
    22. }
    23. }

    img.png

    等待子线程执行完毕 join()

    让主线程等待当前执行中子线程执行完毕。

    [子线程代码]MyThread5.java

    1. package example05_join;
    2. public class MyThread5 extends Thread{
    3. @Override
    4. public void run() {
    5. // super.run();
    6. for(int i = 0;i<=1000;i++){
    7. System.out.println("===> 子线程 " + i);
    8. }
    9. }
    10. }

    [主线程代码]Test5.java

    1. package example05_join;
    2. public class Test {
    3. public static void main(String[] args) {
    4. MyThread5 myThread5 = new MyThread5();
    5. myThread5.start();
    6. for(int i = 0;i<=100;i++){
    7. System.out.println("===> 主线程 " + i);
    8. }
    9. try {
    10. myThread5.join(); //此时让主线程等待子线程执行完毕,再继续执行
    11. }catch (InterruptedException e){
    12. e.printStackTrace();
    13. }
    14. System.out.println("主线程执行完毕");
    15. }
    16. }

    让出CPU资源,让其他线程执行 yield()

    让行,不代表不执行。具体谁先进行,依然由CPU决定,不能完全确定

    [子线程代码]MyThread6.java

    1. package example06_yield;
    2. public class MyThread6 extends Thread {
    3. public MyThread6(String name) {
    4. super.setName(name);
    5. }
    6. @Override
    7. public void run() {
    8. // super.run();
    9. for (int i = 0; i <= 500; i++) {
    10. System.out.println(super.getName() + ":" + i);
    11. if (i % 10 == 0) {
    12. Thread.yield();
    13. }
    14. }
    15. }
    16. }

    [主线程代码]Test6.java

    1. package example06_yield;
    2. import example04_sleep.yield.MyThread6;
    3. public class Test6 {
    4. public static void main(String[] args) {
    5. MyThread6 mt1 = new MyThread6("A");
    6. MyThread6 mt2 = new MyThread6("B");
    7. mt1.start();
    8. mt2.start();
    9. }
    10. }

    [运行结果截图]

    img.png

    正如上图所见,让行是开发者设定的希望情况,但是并不代表一定成功让行。最终的决定权还是由CPU来决定

    打断线程(尤其是正在睡眠中的线程) interrupt

    [子线程代码]MyThread6.java

    1. package example07.interrupt;
    2. public class MyThread7 extends Thread{
    3. @Override
    4. public void run() {
    5. // super.run();
    6. System.out.println("线程即将进行休眠");
    7. try{
    8. Thread.sleep(1000000);
    9. }catch (InterruptedException e){
    10. // e.printStackTrace();
    11. System.out.println("异常:线程休眠被打断");
    12. }
    13. System.out.println("线程被激活");
    14. }
    15. }

    [主线程代码]Test6.java

    1. package example07.interrupt;
    2. public class Test7 {
    3. public static void main(String[] args) {
    4. MyThread7 myThread7 = new MyThread7();
    5. myThread7.start();
    6. for(int i = 0;i<=100;i++){
    7. System.out.println("===> 主线程 " + i);
    8. }
    9. myThread7.interrupt(); // 打断正在休眠中的子线程
    10. }
    11. }

    [运行结果截图]

    img.png

    3.线程同步

    线程同步:当多个线程共享同一个资源的时候,我们可以在某一个线程访问到这个资源的时候,将这个资源暂时封锁
    等待当前线程执行结束,解锁该资源,其他线程才可以来继续执行。

    总结:等待其他线程释放锁
    目的:让线程更加安全

    下面以银行取钱案例说明。
    [账户对象]Account.java

    1. package example08_sync;
    2. public class Account {
    3. private double balance;
    4. public Account(double balance){
    5. this.balance = balance;
    6. }
    7. public void getMoney(){ //在方法声明上添加synchronized关键字 一旦执行到该方法,瞬间锁定对象Account
    8. if(this.balance<=0){return;}
    9. System.out.println("即将取走1000,目前金额为:"+this.balance);
    10. this.balance -= 1000;
    11. System.out.println("成功取走1000,目前金额为:"+this.balance);
    12. }
    13. }

    [取钱线程]GetMoneyThread.java

    1. package example08_sync;
    2. public class GetMoneyThread extends Thread{
    3. private Account acc;
    4. public GetMoneyThread(Account acc){
    5. this.acc = acc;
    6. }
    7. @Override
    8. public void run() {
    9. acc.getMoney();
    10. }
    11. }

    [测试类]Test8.java

    1. package example08_sync;
    2. public class Test8 {
    3. public static void main(String[] args) {
    4. //创建账户
    5. Account account = new Account(1000);
    6. //创建ATM线程
    7. GetMoneyThread atm = new GetMoneyThread(account);
    8. //创建柜台线程
    9. GetMoneyThread table = new GetMoneyThread(account);
    10. //取钱
    11. atm.start();
    12. table.start();
    13. }
    14. }

    [运行结果]

    img.png

    可以发现,设置的余额不足拦截(if(this.balance<=0){return;})没有生效,出现了账户余额为负数情况。

    为避免这些情况的出现,因此需要线程同步。即将多个处理统一资源的线程,进行队列化管理(锁)

    实现线程同步的方法

    方法1:在方法声明上添加一个synchronized关键字

    1. package example08_sync;
    2. public class Account {
    3. private double balance;
    4. public Account(double balance){
    5. this.balance = balance;
    6. }
    7. public synchronized void getMoney(){ //在方法声明上添加synchronized关键字 一旦执行到该方法,瞬间锁定对象Account
    8. if(this.balance<=0){return;}
    9. System.out.println("即将取走1000,目前金额为:"+this.balance);
    10. this.balance -= 1000;
    11. System.out.println("成功取走1000,目前金额为:"+this.balance);
    12. }
    13. }

    方法2:在方法内部添加一个synchronized关键字

    1. package example08_sync;
    2. public class Account {
    3. private double balance;
    4. public Account(double balance){
    5. this.balance = balance;
    6. }
    7. public synchronized void getMoney(){ //在方法声明上添加synchronized关键字 一旦执行到该方法,瞬间锁定对象Account
    8. synchronized (this){
    9. if(this.balance<=0){return;}
    10. System.out.println("即将取走1000,目前金额为:"+this.balance);
    11. this.balance -= 1000;
    12. System.out.println("成功取走1000,目前金额为:"+this.balance);
    13. }
    14. }
    15. }

    方法3:手动上锁

    该方法用的最少,因为容易出现忘记解锁。

    1. package example08_sync;
    2. import java.util.concurrent.locks.Lock;
    3. import java.util.concurrent.locks.ReentrantLock;
    4. public class Account {
    5. private double balance;
    6. private Lock lock = new ReentrantLock(); //创建锁
    7. public Account(double balance){
    8. this.balance = balance;
    9. }
    10. public void getMoney(){ //在方法声明上添加synchronized关键字 一旦执行到该方法,瞬间锁定对象Account
    11. lock.lock(); // 上锁
    12. if(this.balance<=0){return;}
    13. System.out.println("即将取走1000,目前金额为:"+this.balance);
    14. this.balance -= 1000;
    15. System.out.println("成功取走1000,目前金额为:"+this.balance);
    16. lock.unlock(); //解锁
    17. }
    18. }

    死锁

    线程同步时最容易发生的问题

    eg:线程A锁定资源1后,等待访问资源2;线程2锁定资源2后,等待访问资源1。

    [资源对象] ResourceObjec.java

    1. package example09_dead;
    2. public class ResourceObject {
    3. public static Object obj1 = new Object();
    4. public static Object obj2 = new Object();
    5. }

    [线程1] DeadLock1.java

    1. package example09_dead;
    2. public class DeadLock1 extends Thread{
    3. @Override
    4. public void run() {
    5. // super.run();
    6. synchronized (ResourceObject.obj1){
    7. System.out.println("线程1 第一个资源锁定");
    8. try{
    9. //通过休眠使CPU去执行其他线程
    10. Thread.sleep(1);
    11. } catch (InterruptedException e) {
    12. e.printStackTrace();
    13. }
    14. synchronized (ResourceObject.obj2){
    15. System.out.println("线程1 第二个资源锁定");
    16. System.out.println("线程1执行完毕");
    17. }
    18. }
    19. }
    20. }

    [线程2] DeadLock2.java

    1. package example09_dead;
    2. public class DeadLock2 extends Thread{
    3. @Override
    4. public void run() {
    5. // super.run();
    6. synchronized (ResourceObject.obj2){
    7. System.out.println("线程2 第二个资源锁定");
    8. try{
    9. //通过休眠使CPU去执行其他线程
    10. Thread.sleep(1);
    11. } catch (InterruptedException e) {
    12. e.printStackTrace();
    13. }
    14. synchronized (ResourceObject.obj1){
    15. System.out.println("线程2 第一个资源锁定");
    16. System.out.println("线程2执行完毕");
    17. }
    18. }
    19. }
    20. }

    [测试] Test9.java

    1. package example09_dead;
    2. public class Test9 {
    3. public static void main(String[] args) {
    4. DeadLock1 dl1 = new DeadLock1();
    5. DeadLock2 dl2 = new DeadLock2();
    6. dl1.start();
    7. dl2.start();
    8. }
    9. }

    [运行结果]

    img.png

    线程的生命周期

    • [1]创建线程
    • ---[start()]--> [2]就绪状态
    • ---[CPU调度]--> [3]运行状态
      • ---[stop()]--> (跳转到4)
      • ---[IO操作、sleep]--> 阻塞状态 ---[IO结束、sleep()结束]--> (跳转到2)
    • --------------> [4]消亡状态

    生产者消费者模型

    案例:实现对视频进行审查,如果是不良视频,则对视频进行删除
    [视频对象] Video.java

    1. package example10_check;
    2. public class Video {
    3. private String name;
    4. public Video (String name){
    5. this.name = name;
    6. }
    7. public String getName() {
    8. return name;
    9. }
    10. public void setName(String name) {
    11. this.name = name;
    12. }
    13. }

    [检查视频] CheckVideo.java

    1. package example10_check;
    2. import java.util.concurrent.BlockingQueue;
    3. import java.util.concurrent.atomic.AtomicInteger;
    4. public class CheckVideo extends Thread{
    5. private static AtomicInteger i = new AtomicInteger(); //AtomicInteger原子型整数。特点:线程安全 效果等同于 int i = 0,但是比int i = 0 更安全。
    6. //将视频添加到缓冲区(队列)
    7. private BlockingQueue
    8. public CheckVideo(BlockingQueue{
    9. this.videos = videos;
    10. }
    11. @Override
    12. public void run() {
    13. while (true){
    14. String name = "不良视频"+i.incrementAndGet();// 相当于i++
    15. Video v = new Video(name);
    16. //将视频添加到队列
    17. try {
    18. System.out.println("发现不良视频"+name);
    19. videos.put(v);
    20. Thread.sleep(200);
    21. } catch (InterruptedException e) {
    22. e.printStackTrace();
    23. }
    24. }
    25. }
    26. }

    [删除视频] DelVideo.java

    1. package example10_check;
    2. import java.util.TreeMap;
    3. import java.util.concurrent.BlockingQueue;
    4. public class DelVideo extends Thread {
    5. private BlockingQueue
    6. public DelVideo(BlockingQueue{
    7. this.videos = videos;
    8. }
    9. @Override
    10. public void run() {
    11. while(true){
    12. try{
    13. Video video = videos.take();
    14. System.out.println("删除不良视频"+video.getName());
    15. Thread.sleep(100);
    16. } catch (InterruptedException e) {
    17. e.printStackTrace();
    18. }
    19. }
    20. }
    21. }

    [测试] Test10.java

    1. package example10_check;
    2. import java.util.concurrent.BlockingQueue;
    3. import java.util.concurrent.LinkedBlockingQueue;
    4. public class Test10 {
    5. public static void main(String[] args) {
    6. BlockingQueue
    7. //创建3个检查视频的线程
    8. CheckVideo ck1 = new CheckVideo(videos);
    9. CheckVideo ck2 = new CheckVideo(videos);
    10. CheckVideo ck3 = new CheckVideo(videos);
    11. //创建两个删除视频的线程
    12. DelVideo d1 = new DelVideo(videos);
    13. DelVideo d2 = new DelVideo(videos);
    14. ck1.start();
    15. ck2.start();
    16. ck3.start();
    17. d1.start();
    18. d2.start();
    19. }
    20. }

    [运行结果]

    img.png

  • 相关阅读:
    机器学习模型3——支持向量机SVM
    【OpenCV-Python-课程学习(贾)】OpenCV3.3课程学习笔记:图像色彩空间的转换(cvtColor、)
    【HTML学生作业网页】基于HTML+CSS+JavaScript仿南京师范大学泰州学院(11页)
    【如何学习CAN总线测试】——OSEK网络管理测试
    1-7Vmware中的快照与克隆
    Vagrant 搭建虚拟机环境
    数据结构day44
    负载均衡(SLB与ELB)
    ERROR 6400 --- [ main] com.zaxxer.hikari.pool.HikariPool : root - Exception
    sql报错:sql injection violation, syntax error
  • 原文地址:https://blog.csdn.net/ks2686/article/details/126527370