【Java多线程讲解-下】搞懂多线程看这一篇就够了

前言:在当今的互联网行业中,多线程技术已经成为了一项非常重要的技能。随着计算机硬件的发展,越来越多的程序员开始关注多线程技术,希望通过多线程来提高程序的性能。Java作为一种广泛使用的编程语言,也提供了丰富的多线程支持。本文将详细介绍Java多线程的基本概念、原理、实现方法以及在生活中的应用,帮助读者更好地理解和掌握Java多线程技术。

在这里插入图片描述

1.线程属性

1.线程中断
中断线程的方法有以下三种

  • 线程运行结束,自然结束
  • 出现了方法中未捕获到异常,终止线程
  • 使用stop方法(已经被废弃了)
  • 使用interrupt方法设置线程的中断状态。 要想得出是否设置了中断状态,首先调用静态的Thread.currentThread 方法获得当前线程, 然后调用isInterrupted 方法。但 是 , 如 果 线 程 被 阻 塞 , 就 无 法 检 查 中 断 状 态。 这 里 就 要 引 人 Interrupt Exception异 常 。 当 在 一 个 被sleep 或 wait调 用 阻 塞 的 线 程 上 调 用 interrupt 方 法 时 , 那 个 阻 塞 调 用 (即 s l e e p 或 wait调用)将被一个InterruptedException异常中断。
class test implements Runnable {
    
    

    public void run() {
    
    
      try{
    
    
        while(true){
    
    
         System.out.println("在运行");
             Thread.sleep(1000);

      }catch(InterruptedException e){
    
    
        
          Thread.currentThread().interrupt();

      }
    }
}

2.守护线程

将 一 个 线 程 转 换 为 守 护 线 程 (d a e m o n t h r e a d ) 。 这 样 — 个 线 程 并 没 有 什 么 魔 力 。 守 护 线 程 的 唯 一用途是为其他线程提供服务。计时器线程就是一个例子,它定时地发送 “计时器嘀嗒” 信 号给其他线程,另外清空过时缓存项的线程也是守护线程。当只剩下守护线程时,虚拟机就 会退出。因为如果只剩下守护线程,就没必要继续运行程序 了。看下面这个例子。

class Counter {
    
    
    //线程个数
    private int count;

    public synchronized int getCount() {
    
    
        return count;
    }
//增加线程个数
    public synchronized void incrementCount() {
    
    
        count++;
    }
//减少线程个数
    public synchronized void decrementCount() {
    
    
        count--;
    }
}

//显示当前线程个数
class Display implements Runnable {
    
    
    private Counter counter;

    public Display(Counter counter) {
    
    
        this.counter = counter;
    }

    @Override
    public void run() {
    
    
        while (true) {
    
    
            try {
    
    
                System.out.println("Number of threads: " + counter.getCount());
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
             break;
            }
        }
    }
}

public class Main {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        final Counter counter = new Counter();
        Thread displayThread = new Thread(new Display(counter));
        displayThread.start();
        for (int i = 0; i < 10; i++) {
    
    
            Thread.sleep(400);
            new Thread(new Runnable() {
    
    
                @Override
                public void run() {
    
    
                    //每创建一个线程就要更新counter
                    counter.incrementCount();
                    try {
    
    
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                    //每结速一个线程也要更新counter
                    counter.decrementCount();
                }
            }).start();
        }
        Thread.sleep(10000);
        System.out.println("main线程已退出");

    }
}

在这里插入图片描述
从这段代码可以看出,如果未将计数器线程设成守护线程,那么主线程结束后,只要计数器线程在运行,则程序也还在运行。当主线程结束后,我们就不需要计数器了,这个时候守护线程的就派上用场了。通过displayThread.setDaemon(true);将displayThread线程设置成守护线程。

2.线程同步于互斥

在大多数实际的多线程应用中,两个或两个以上的线程需要共享对同一数据的存取。如 果两个线程存取同一个对象,并且每个线程分别调用了一个修改该对象状态的方法,会发生 什么呢?可以想见,这两个线程会相互後盖。取决于线程访问数据的次序,可能会导致对象 被破坏 。这种情况通常称为竞态条件(racecondition)。
在多线程环境下,为了保证数据的一致性和完整性,需要对共享资源进行同步和互斥访问。Java提供了synchronized关键字来实现同步,通过synchronized关键字修饰的方法或者代码块,同一时刻只能有一个线程访问。此外,Java还提供了Lock接口及其实现类(如ReentrantLock)来实现互斥访问。
先看一个例子:

 class  test{
    
    

    public static void main(String[] args) {
    
    
        account account = new   account(5000);
        Thread a = new Thread(()->{
    
    
           while (account.getCash()>=500){
    
    

               try {
    
    
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
    
    
                   throw new RuntimeException(e);
               }
               account.drawMoney(500);
               System.out.println("余额:"+account.getCash());
           }
        });
        Thread b = new Thread(()->{
    
    
            while (account.getCash()>=1000){
    
    

                try {
    
    
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
    
    
                    throw new RuntimeException(e);
                }
                account.drawMoney(1000);
                System.out.println("余额:"+account.getCash());
            }
        });
        a.start();
        b.start();


    }
}
     class account{
    
    
    //账户余额
    private  int cash;
    public account(int cash){
    
    
        this.cash=cash;
    }
    //取钱
    public  void drawMoney(int money){
    
    
        this.cash-=money;
    }

    public int getCash() {
    
    
        return cash;
    }

    public void setCash(int cash) {
    
    
        this.cash = cash;
    }
}

运行结果:
在这里插入图片描述
在上面这个例子,a和b同时去取同一个账户的钱,因为二人可以同时操作账户对象,假如此时余额只有1000元,两个人都很自私,都想着干就完了,最后两人都取到了钱,但银行却亏了500元。所以这种并发地访问同一个对象尤其是要进行修改操作时,很不安全。
解决方案一:
添加synchronized注解
同步方法:将直接加到方法前面

   public synchronized void drawMoney(int money){
    
    
        this.cash-=money;
    }

同步块

 Thread a = new Thread(()->{
    
    
          synchronized (account){
    
    
              while (true){
    
    
                 if (account.getCash() >= 1000) {
    
    

                    try {
    
    
                  
                        Thread.sleep(1000);
                        account.drawMoney(1000);
                    } catch (InterruptedException e) {
    
    
                        throw new RuntimeException(e);
                    } 
                

                    System.out.println("余额:" + account.getCash());
                }
                else break;
          }
        });

synchronized(要修改的对象){
要同步的代码
}
不管用哪种方法,都是将synchronized加在要修改的对象或方法上

 Thread b = new Thread(() -> {
    
    

            while (true) {
    
    
                if (account.getCash() >= 500) {
    
    
                    try {
    
    
                    //获得锁
                        lock.lock();
                        Thread.sleep(1000);
                        account.drawMoney(500);
                    } catch (InterruptedException e) {
    
    
                        throw new RuntimeException(e);
                    } finally {
    
    
                    //释放锁
                        lock.unlock();
                    }
                    System.out.println("余额:" + account.getCash());

                }
              else break;
            }
        });

本期内容就到这里,如果觉得写的不错的话可以关注下一期。
下期预告:在这里插入图片描述

线程间通信
线程池的使用
Java多线程在生活中的应用

猜你喜欢

转载自blog.csdn.net/weixin_55939638/article/details/134579110