Java多线程,原来如此简单?

学习大纲

  • 线程简介
  • 线程实现(重点)
  • 线程状态
  • 线程同步(重点
  • 线程通信问题
  • 高级主题(线程池)

一、线程简介

1. 多任务

在生活中,我们一般会边吃饭边玩手机,或者边走路边玩手机,这样看起来我们在同时做着多个事情,但是其实我们大脑在同一时间只能处理一件事情,

2.多线程

例子:原来一条路,车太多了,开始堵塞,为了提高效率,我们多增加个车道,从此,妈妈在也不用担心道路阻塞了。玩王者荣耀也是如此,你和队友组队,大家都是多线程执行。
在这里插入图片描述

3.进程

在操作系统的运行的程序就是进程。一个进程可以有多个线程。

  • 程序是指令和数据的有序集合,是静态的。
  • 进程是程序的一次执行过程,是动态的,是系统资源分配的单位。
  • 一个进程可以包含多个线程,但至少有一个线程,线程是cpu执行和调度的基本单位。

Java中的多线程是模拟出来的,真的的多线程是指有多个CPU,即 多核,比如 服务器。而模拟出来的cpu,在同一个时间点,cpu只能执行一个代码,因为cpu调度,不同线程切换很快,所以就有同时执行的错觉。

总结来说,就是宏观上并行,微观上串行。
在这里插入图片描述

二、线程实现(Thread,Runnable、Callable)

1、三种方式:
  • Thread class ------> 继承 Thread 类 (重点)
  • Runnable 接口 -----> 实现Runnable 接口(重点)
  • Callable 接口 ------> 实现Callable 接口
2.实现代码学习

(1)创建线程方式一:继承Thread 类,重写 run() 方法,调用start()方法开启线程(注意,线程开启不一定执行,由cpu进行调度)

package thread;

/**
 * @Author Janson
 * @Date 2022/2/24 9:24
 * @Version 1.0
 */
// 注意,线程开启不一定执行,由cpu进行调度
//创建线程方式一:继承Thread 类,重写 run() 方法,调用start()方法开启线程
public class TestThread extends Thread {
    @Override
    //重写 run() 方法
    public void run(){
        for (int i = 0; i < 200; i++) {
            System.out.println("我在看代码-----"+i);
        }
    }

    public static void main(String[] args)  {
        //main 线程,主线程
        TestThread testThread = new TestThread();
        //testThread.run();
        testThread.start();
        for (int i = 0; i < 20; i++) {
            System.out.println("我在学习多线程:" + i );
            System.out.println();
        }
    }
}

网图下载例子:

下载图片的类 借助 commons io 包中的 FileUtils 类的 copyURLToFile方法进行 下载 图片

package thread;

import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
/**
 * @Author Janson
 * @Date 2022/2/24 12:32
 * @Version 1.0
 */
public class TestThreadDownlPic extends Thread {
    private String name;
    private String url;
    //构造方法,调用的时候用于传递初始参数
    public TestThreadDownlPic(String url,String name) {
        this.name = name;
        this.url = url;
    }
    //重写run方法
    @Override
    public void run(){
        //下载图片的线程的执行体
        WebDownload webDownload = new WebDownload();
        webDownload.downloader(url,name);
        System.out.println("下载了文件名为:" + name);
    }
    //主线程 main 方法
    public static void main(String[] args) {
        //创建 线程 对象
        TestThreadDownlPic testThreadDownlPic1 = new TestThreadDownlPic(
                "https://www.todesk.com/image/download/windows_pic.png",
                //C:\Users\25393\Desktop\img\  该地址为路径,1.jpg为文件名
                "C:\\Users\\25393\\Desktop\\img\\1.jpg");
        //如果不写路径,则默认在项目根路径下
        TestThreadDownlPic testThreadDownlPic2 = new TestThreadDownlPic(
                "https://www.todesk.com/image/logo-text.png",
                "2.jpg");
        TestThreadDownlPic testThreadDownlPic3 = new TestThreadDownlPic(
                "https://www.todesk.com/image/news/1.jpg",
                "3.jpg");
        //启动线程
        testThreadDownlPic1.start();
        testThreadDownlPic2.start();
        testThreadDownlPic3.start();
    }

}
//下载图片的类
class WebDownload{
    public void downloader(String url,String name){
        try {
            //借助 commons io 包中的 FileUtils 类的 copyURLToFile方法进行 下载 图片
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("执行webdownload类,出现io异常。");
        }
    }
}

(2)创建线程方式2 ,实现Runnable接口,重写run() 方法

实现Runnable 接口 实现线程时,需要用Thread 类 的代理进行启动线程,即 将Runnable 接口的实现类的对象,作为Thread 类的参数,创建对象,用该对象启动线程

package thread;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

/**
 * @Author Janson
 * @Date 2022/2/24 14:48
 * @Version 1.0
 */
public class TestThreadRunnable implements Runnable {
    private String url;
    private String name;
    public TestThreadRunnable(String url,String name){
        this.name = name;
        this.url = url;
    }
    @Override
    public void run() {
        //下载图片的线程的执行体
        WebDownload webDownload = new WebDownload();
        webDownload.downloader(url,name);
        System.out.println("下载了文件名为:" + name);
    }
    public static void main(String[] args) {
        //创建 线程 对象
         TestThreadRunnable testThreadRunnable1= new TestThreadRunnable(
                "https://www.todesk.com/image/download/windows_pic.png",
                //C:\Users\25393\Desktop\img\  该地址为路径,1.jpg为文件名
                "C:\\Users\\25393\\Desktop\\img\\1.jpg");
        //如果不写路径,则默认在项目根路径下
        TestThreadRunnable testThreadRunnable2= new TestThreadRunnable(
                "https://www.todesk.com/image/logo-text.png",
                "2.jpg");
        TestThreadRunnable testThreadRunnable3= new TestThreadRunnable(
                "https://www.todesk.com/image/news/1.jpg",
                "3.jpg");
        //启动线程, 继承 thread 实现线程时,直接用实现类的对象 调用start方法启动线程
        //  实现Runnable 接口 实现线程时,需要用Thread 类 的代理进行启动线程,
        //  即 将Runnable 接口的实现类的对象,作为Thread 类的参数,创建对象,用该对象启动线程
        new Thread(testThreadRunnable1).start();
        new Thread(testThreadRunnable2).start();
        new Thread(testThreadRunnable3).start();
    }
}
class WebDownload2{
    public void downloader(String url,String name){
        try {
            //借助 commons io 包中的 FileUtils 类的 copyURLToFile方法进行 下载 图片
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("执行webdownload类,出现io异常。");
        }
    }
}

3、小结

在这里插入图片描述

4. 龟兔赛跑
package thread;

/**
 * 龟兔赛跑
 * @Author Janson
 * @Date 2022/2/24 15:57
 * @Version 1.0
 */
public class Race implements Runnable {
    private String winner;

    @Override
    public void run() {
        for (int i=1;i<=100;i++){
            //让兔子线程 sleep
            if (Thread.currentThread().getName()=="兔子" && i%10==0){
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println("线程 sleep 异常");
                }
            }
            System.out.println(Thread.currentThread().getName() + "跑了"+ i +"步");
            //程序终止条件
            boolean flag = gameOver(i);
            if (flag){
                break;
            }
        }
    }
    //判断是否产生了赢家,产生了,程序终止
    private boolean gameOver(int steps){
        //必须要判断winner 是否为空,不判断的话,程序会将另一个线程执行结束才终止
        if (winner != null){
            return true;
        }else if (steps>=100){
            winner = Thread.currentThread().getName();
            System.out.println("最后的赢家:"+ winner);
            return true;
        }
        return false;
    }
    //main方法  主线程
    public static void main(String[] args) {
        Race race = new Race();
         new Thread(race, "乌龟").start();
        Thread rabbit = new Thread(race, "兔子");
        //设置线程的优先级
        //rabbit.setPriority(Thread.MAX_PRIORITY);
        rabbit.start();
    }
}

5、实现Callable 接口
  • 重写call方法
  • 创建线程池对象,用submit 方法启动线程,有返回值
package thread;

import java.util.concurrent.*;

/**
 * @Author Janson
 * @Date 2022/2/24 19:56
 * @Version 1.0
 */
public class TestCallable implements Callable {

    @Override
    public Object call() throws Exception {
        System.out.println("执行多线程了:");
        return 1;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestCallable testCallable = new TestCallable();
        ExecutorService executorService = Executors.newCachedThreadPool();
        Future submit = executorService.submit(testCallable);
        System.out.println(submit.get());
        executorService.shutdownNow();
    }
}

6.静态代理模式
  • 真实对象和代理对象都要实现同一个接口
  • 代理对象要代理真实对象,即 在代理对象中调用真实对象
package thread;

/**
 * @Author Janson
 * @Date 2022/2/24 21:16
 * @Version 1.0
 */
// 静态代理模式总结:
    //真实对象和代理对象都要实现同一个接口
    //代理对象要代理真实对象,即 在代理对象中调用真实对象
public class StaticProxy {
    public static void main(String[] args) {
        //You you = new You();
        //WeddingCompany weddingCompany = new WeddingCompany(you);
        //weddingCompany.happyMarry();
        //下边一行 ,和上边三行功能一样
        new WeddingCompany(new You()).happyMarry();
        //new WeddingCompany(()-> System.out.println("我爱你")).happyMarry();
    }
}
// 结婚接口
interface Marry{
    void happyMarry();
}
//结婚对象  You
class You implements Marry{
    @Override
    public void happyMarry() {
        System.out.println("janson要娶媳妇了。");
    }
}
//代理公司,代理你办理一切事宜
class WeddingCompany implements Marry{
    private Marry target;
    public WeddingCompany(Marry marry) {
        this.target = marry;
    }
    @Override
    public void happyMarry() {
        before();
        target.happyMarry(); //执行真实的对象
        after();
    }
    private void after() {
        System.out.println("结婚后,睡媳妇");
    }
    private void before() {
        System.out.println("结婚前,接媳妇");
    }
}

7、Lambda表达式

(1)简介
在这里插入图片描述
在这里插入图片描述

  • 函数式接口:
    *任何接口,如果只包含一个抽象方法,那么它就是一个函数式接口。
    *对于函数式接口,我们可以通过 lambda 表达式来创建该接口的对象,简化编程

  • 匿名内部类与lambda表达式存在相似之处:
    * = 前边直接用 接口 创建对象,在 = 后边,不一样,
    *匿名内部类时 new 接口{ 重写接口中的方法};
    *lambda 表达式 是 ()->{需要执行的语句};
    *注意:两个类的 } 后边都由 ; ,不要忘记。

(2)代码示例

package thread;
//为什么要使用 lambda 表达式
    //避免匿名内部类定义过多
    //可以让代码看起来更简洁
    //去掉了一堆没有意义的代码,只留下核心的代码
/**
 * @Author Janson
 * @Date 2022/2/25 10:26
 * @Version 1.0
 */
/**
 *函数式接口:
        *任何接口,如果只包含一个抽象方法,那么它就是一个函数式接口。
        *对于函数式接口,我们可以通过 lambda 表达式来创建该接口的对象,简化编程
 */
public class TestLambda {
    public static void main(String[] args) {
        //外部类实现接口
        Like1 like1 = new Like1();
        like1.lambda();
        //内部类实现接口
        Like2 like2 = new Like2();
        like2.lambda();

        //3.局部内部类
        class Like3 implements ILike{
            @Override
            public void lambda() {
                System.out.println("I like lambda3");
            }
        }
        Like3 like3 = new Like3();
        like3.lambda();
        /**
         * 匿名内部类与lambda表达式存在相似之处:
                * = 前边直接用 接口 创建对象,在 = 后边,不一样,
                *匿名内部类时 new 接口{ 重写接口中的方法};
                *lambda 表达式 是 ()->{需要执行的语句};
                *注意:两个类的 } 后边都由 ; ,不要忘记。
         */
         //4.匿名内部类,没有类名称,用接口进行创建
         ILike like4 =   new ILike() {
            @Override
            public void lambda() {
                System.out.println("I like lambda4");
            }
        };
        like4.lambda();
        //5.用lambda 表达式
        ILike like5 = () -> {
            System.out.println("I like lambda5");
        };
        like5.lambda();

    }
    //2.内部类
    static class Like2 implements ILike{
        @Override
        public void lambda() {
            System.out.println("I like lambda2");
        }
    }
}

//定义一个函数式接口
interface ILike{
    void lambda();
}
//1.实现类  外部类
class Like1 implements ILike{
    @Override
    public void lambda() {
        System.out.println("I like Lambda1");
    }
}


三、线程状态(五个状态)

  • 创建

  • 就绪

  • 运行

  • 阻塞

  • 死亡
    (1)状态讲解
    在这里插入图片描述
    在这里插入图片描述
    (2)线程方法
    在这里插入图片描述
    (3)线程停止

  • 1.建议线程正常停止 ——————> 利用次数,不建议死循环

  • 2.建议使用标志位------> 设置一个标志位

  • 3.不要使用stop 或者 destory 等过时 或者不建议使用的方法

在这里插入图片描述
代码实现:

package thread;

/**
 * @Author Janson
 * @Date 2022/2/25 12:26
 * @Version 1.0
 */
//测试 stop
//    1.建议线程正常停止 ——————> 利用次数,不建议死循环
//    2.建议使用标志位------> 设置一个标志位
//    3.不要使用stop 或者 destory 等过时 或者不建议使用的方法
public class ThreadStop  implements Runnable{
    private  boolean flag = true;
    public static void main(String[] args) {
        ThreadStop threadStop = new ThreadStop();
        new Thread(threadStop).start();

        for (int i = 0; i <1000 ; i++) {
            System.out.println("main线程" + i);
            if (i==900){
                threadStop.stop();
                System.out.println("线程该停止了");
            }
        }

    }
    //写一个公开的方法停止线程
    public  void stop() {
        this.flag = false;
    }
    @Override
    public void run() {
        int i = 0;
        while (flag){
            System.out.println("run ...... Thread..." + i++);
        }
    }
}

(4)线程 休眠 sleep

  • 作用:模拟网络延时, 放大问题的发生性
  • sleep(时间)指定当前线程阻塞的毫秒数;
  • sleep存在异常InterruptedException;
  • sleep时间达到后线程进行就绪状态
  • sleep可以模拟网络延时,倒计时等;
  • 每一个对象都有一个锁,sleep不会释放锁;

代码:倒计时例子

package thread;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @Author Janson
 * @Date 2022/2/25 15:23
 * @Version 1.0
 */
//模拟网络延时, 放大问题的发生性
public class TestThreadSleep implements Runnable{
    @Override
    public void run() {
        while (true){
            int i = 10;
            for (; i >0; i--) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(i);
            }
            if (i == 0)
                break;;
        }

    }

    public static void main(String[] args) throws InterruptedException {
        //TestThreadSleep testThreadSleep = new TestThreadSleep();
        //new Thread(testThreadSleep).start();
        Date date = new Date(System.currentTimeMillis());
        while (true){
            //ms 级别
            Thread.sleep(1000);
            int i = 0;
            System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
            i++;
            date = new Date(System.currentTimeMillis());
            if (i == 10)
                break;
        }
    }
}

(5)线程礼让 yield

  • 礼让线程,让当前正在执行的线程暂停,但不阻塞;
  • 将线程从运行状态转为就绪状态;
  • 让cpu重写调度,礼让不一定成功,看cpu心情。

代码

package thread;

/**
 * @Author Janson
 * @Date 2022/2/27 12:49
 * @Version 1.0
 */
public class TestThreadYield implements Runnable{
    @Override
    public void run() {
        System.out.println("副线程执行前----");
        System.out.println("副线程执行后----");
    }

    public static void main(String[] args) {
        TestThreadYield testThreadYield = new TestThreadYield();
        new Thread(testThreadYield).start();

        System.out.println("main 线程执行前----");
        //主线程先执行,遇到该命令,主线程就会转为 就绪 状态,与 副线程 重写开始竞争cpu
        //谁竞争到,谁就先执行
        Thread.yield();
        System.out.println("main 线程执行后----");
    }
}

(6)线程强制执行 join

  • Join 合并线程,待此线程执行完成后,在执行其他线程,其他线程阻塞
  • 类似于插队
  • 抢占方式,一旦该线程启动,就会抢占cpu,直到该线程执行结束

代码实现:

package thread;

/**
 *  join() 抢占方式,一旦该线程启动,就会抢占cpu,直到该线程执行结束
 * @Author Janson
 * @Date 2022/2/27 12:42
 * @Version 1.0
 */
public class TestThreadJoin implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i <200 ; i++) {
            System.out.println("测试线程执行------");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        TestThreadJoin testThreadJoin = new TestThreadJoin();
        new Thread(testThreadJoin).start();
        //抢占cpu,接下来 cpu 只执行该线程,而不执行主线程,直至该线程执行结束
        new Thread(testThreadJoin).join();
        for (int i = 0; i <1000 ; i++) {
            System.out.println("main线程执行------");
        }
    }
}

(6)线程状态(Thread.State())
在这里插入图片描述
(7)线程优先级

  • Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度按个线程来执行。
  • 线程优先级用数字表示,范围1-10;
  • Thread.MIN_PRIORITY = 1;
  • Thread.MAX_PRIORITY = 10;
  • Thread.NORM_PRIORITY = 5;
  • 使用以下方法改变或获取优先级,优先级 设置要 先于线程启动
  • getPriority() , setPriority(int xxx),

代码实现:

package thread;

/**
 * 优先级设置
 * @Author Janson
 * @Date 2022/2/27 12:56
 * @Version 1.0
 */
public class TestThreadPriority implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+
                    "线程的优先级为:"+
                    Thread.currentThread().getPriority()+"执行次数" + i);
        }
    }
    public static void main(String[] args) {
        TestThreadPriority testThreadPriority = new TestThreadPriority();
        //线程1
        Thread thread1 = new Thread(testThreadPriority,"1线程");
        thread1.setPriority(Thread.MAX_PRIORITY);
        thread1.start();
        //线程2
        Thread thread2 = new Thread(testThreadPriority,"2线程");
        thread2.setPriority(8);
        thread2.start();
        //线程3
        Thread thread3 = new Thread(testThreadPriority,"3线程");
        thread3.setPriority(4);
        thread3.start();
        //线程4
        Thread thread4 = new Thread(testThreadPriority,"4线程");
        thread4.setPriority(1);
        thread4.start();
        //线程5
        Thread thread5 = new Thread(testThreadPriority,"5线程");
        thread5.setPriority(6);
        thread5.start();
        System.out.println("main线程的优先级为:" + Thread.currentThread().getPriority());
    }
}

(8)守护线程 Daemon

  • 线程分为用户线程和守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕
  • 如,后台记录操作日志,监控内存,垃圾回收等待。
  • 守护线程会一直执行到所有线程终止,守护线程才会慢慢停止

代码实现:

package thread;

/**
 * 守护进程
 * 线程分为 用户线程 和 守护线程,setDaemon() 中的参数默认为false,既默认线程为用户线程
 * 守护线程会一直执行到所有线程终止,守护线程才会慢慢停止
 * 用户线程就是执行自己的生命周期
 * @Author Janson
 * @Date 2022/2/27 14:50
 * @Version 1.0
 */
public class TestThreadDaemon  {
    public static void main(String[] args) {
        God god = new God();
        Person person = new Person();
        Thread thread = new Thread(god);
        //This method must be invoked before the thread is started.
        thread.setDaemon(true);
        thread.start();
        new Thread(person).start();
    }
}
class God implements Runnable{
    @Override
    public void run() {
        while (true){
            System.out.println("上帝守护者你");
        }
    }
}
class Person implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("人的一生要活得快快乐乐------" + i);
        }
        System.out.println("-----------good bye-----------world");
    }

}

四、线程同步(synchronized,ReentrantLock,两种锁机制)

(1)锁机制

多个线程操作同一个资源时,造成线程不安全问题,例如上万人抢一百张票,就会出现数据紊乱问题,就需要线程同步解决问题。

  • 采用线程锁,synchronized ,既可以锁方法,又可以 锁代码块
  • ReentrantLock 只能锁代码块
synchronized (obj){
            //需要锁的执行体
        }

在这里插入图片描述

代码实现:买票案例

package thread.sysChronize;

/**
 * 线程同步
 * @Author Janson
 * @Date 2022/2/27 15:45
 * @Version 1.0
 */
public class TestThreadSysChronized {
    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();
        new Thread(buyTicket,"小黑").start();
        new Thread(buyTicket,"小白").start();
        new Thread(buyTicket,"小黄牛").start();
        //String name = buyTicket.getClass().getName();
        //System.out.println(name);
        //String simpleName = buyTicket.getClass().getSimpleName();
        //System.out.println(simpleName);
    }
}
class BuyTicket implements Runnable{
    private int ticket = 10;
    //线程停止标志位
    private boolean flag  = true;
    @Override
    public void run() {
        while (flag){
            buy();
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    //采用线程锁,synchronized ,既可以锁方法,又可以 锁代码块
    public  synchronized void buy(){
        //判断是否有票
        if (ticket<=0){
            flag = false;
            return;
        }
        System.out.println(Thread.currentThread().getName() + "买到了第 " + ticket-- + " 票");

    }
}


代码实现二——银行转账案例

package thread.sysChronize;

import java.util.Arrays;

/**
 * 银行账户转账案例,学习 线程不安全情况下, 加锁解决线程不安全问题
 * @Author Janson
 * @Date 2022/2/28 18:54
 * @Version 1.0
 */
public class TestSyncBank{
    private static final int ACCOUNTS = 100;
    private static final int INITIALBALANCE = 1000;
    private static final int MAXAMMOUNT = 1000;

    public static void main(String[] args) {
        Bank bank = new Bank(ACCOUNTS,INITIALBALANCE);
        //可重入锁,在run方法中加,或者 在 lambda 表达式中加上,
        //ReentrantLock reentrantLock = new ReentrantLock();
        for (int i = 0; i < ACCOUNTS; i++) {
                int fromaccount = i;
                //调用线程接口,采用lambda 表达式进行,也可以实现通过该接口,重写run方法
                Runnable runnable = ()->{
                    //reentrantLock.lock();
                        int  toaccount = (int) (ACCOUNTS * Math.random());
                        int amount = (int) (MAXAMMOUNT * Math.random());
                        //调用转账方法
                        bank.transfer(amount,fromaccount,toaccount);
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();

                    }finally {
                            //reentrantLock.unlock();
                        }
                };
                new Thread(runnable).start();
            }
    }
}

class Bank{
    private int[] account;
    private int initBalance;

    public Bank(int account,int initBalance) {
        this.account = new int[account];
        this.initBalance = initBalance;
        Arrays.fill(this.account,this.initBalance);
    }
    //转账方法
    //给 transfer 加锁synchronized,不然会出现线程不安全 ,加锁在方法上
    public synchronized void transfer(int amount,int fromaccount,int toaccount){
        System.out.println(Thread.currentThread());
        if (account[fromaccount] >= amount){
            account[fromaccount] -= amount;
        }else
            return;
        System.out.printf("%d from %d to %d\n",amount,fromaccount,toaccount);
        account[toaccount] += amount;
        System.out.println("各个账户的总余额:" + getTotalBalance());
    }
    //获取所有账户的余额,正常来说 是 100000
    private int getTotalBalance() {
        int sum = 0;
        for (int i = 0; i < account.length; i++) {
            sum += account[i];
        }
        return sum;
    }
}

(2)死锁

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

在这里插入图片描述

五、线程通信问题

六、线程池

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Janson666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值