Java多线程(超详细+大量代码)

简介: 程序:一段静态的代码进程:运行中的代码线程:程序的一种执行路径
🏠个人主页: 黑洞晓威
🧑个人简介:大家好,我是晓威,一名普普通通的大二在校生,希望在CSDN中与大家一起成长。

🎁如果你也在正在学习Java,欢迎各位大佬来到我的博客查漏补缺呀,如果有哪里写的不对的地方也欢迎诸佬指正啊。

@[toc]

线程

程序:一段静态的代码

进程:运行中的代码

线程:程序的一种执行路径

线程A和线程B是什么关系?

在java语言中:

线程A和线程B,堆内存方法区 内存共享。但是 栈内存 独立一个线程一个栈

线程的创建方法一(继承)

  1. 创建一个继承与Thread的子类
  2. 重写Thread的run()方法
  3. 创建Thread类子类的对象
  4. 通过这个对象调用start()
class  mythresd extends Thread{//创建一个继承与Thread的子类
    @Override
    public void run() {//重写Thread的run()方法
        for(int i = 0;i<=100;i++){
            if(i%2==0){
                System.out.println(("分支线程--->" + i);
            }
        }
    }
}
public class App {
    public static void main(String[] args) {
        mythresd mythresd = new mythresd();创建Thread类的子类对象
      // 不会启动线程,不会分配新的分支栈。(这种方式就是单线程。)
       //mythresd.run();
            
        mythresd.start();//通过这个对象调用start()
            
        for(int i = 0;i<=100;i++){
            if(i%2==0){
                System.out.println(("主线程--->" + i);
            }
        }

    }

}

注意:

  • t.run() 不会启动线程,只是普通的调用方法而已。不会分配新的分支栈。(这种方式就是单线程。)
  • t.start() 方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了。
    这段代码的任务只是为了开启一个新的栈空间,只要新的栈空间开出来,start()方法就结束了。线程就启动成功了。启动成功的线程会自动调用run方法,并且run方法在分支栈的栈底部(压栈)。
  • run方法在分支栈的栈底部,main方法在主栈的栈底部。run和main是平级的。匿名创建Thread类子类

匿名创建Thread类子类

public static void main(String[] args) {
    new Thread(){
        @Override
        public void run() {
            for(int i = 0;i<=100;i++){
                if(i%2==0){
                    System.out.println(i);
                }
            }
        }
    }.star();

Thread中的常用方法

  1. currentThread():静态方法,返回当前代码的线程
  2. getName():获得当先线程的名字
  3. setName():设置当前线程的名字
  4. yield():释放当前cpu的执行权
  5. join():在线程a中调用线程b的join();则线程a进入阻塞状态,等线程b结束后结束阻塞状态
  6. sleep(Long militime)让当前线程阻断指定的militime毫秒,此时为阻塞状态
  7. isAlive();判断线程是否存活
class  mythresd extends Thread{
    @Override
    public void run() {
        for(int i = 0;i<=100;i++){
             try {
                    sleep(100);//阻断100毫秒
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            if(i%2==0){
                System.out.println(Thread.currentThread().getName()+" "+i);//输出线程名
            }
        }
    }
}
public class App {
    public static void main(String[] args) {
        mythresd mythresd = new mythresd();
        mythresd.setName("线程一");//设置线程名
        mythresd.start();

        Thread.currentThread().setName("主线程");
        for(int i = 0;i<=100;i++){
            if(i%2==0){   
                   if(i==20){
                    mythresd.join();
                } //阻断主线程
                System.out.println(Thread.currentThread().getName()+" "+i);//设置主线程的名字
            }
        }
        //判断mythread线程是否还存活
        System.out.println(mythresd.isAlive());
    }

}

线程的优先级

MAX_PRIORITY:10

MIN_PRIORITY:1

NORM_PRIORITY:5(默认)

注意:高优先级抢占低优先级线程cpu,但不代表一定先执行完高优先级再执行低优先级

实例:三个窗口卖一百张票

class  piao extends Thread{
    private static int tickect=100;
    @Override
    public void run() {
        while (tickect>0){
            System.out.println(getName()+"卖了"+tickect);
            tickect=tickect-1;
        }
    }
}
public class App {

    public static void main(String[] args) throws InterruptedException {
        piao p1 = new piao();
        //给p1设定优先级
        p1.setPriority(Thread.MAX_PRIORITY);
        piao p2 = new piao();
        piao p3 = new piao();

        
        p1.setName("窗口一");
        p2.setName("窗口二");
        p3.setName("窗口三");

        p1.start();
        p2.start();
        p3.start();
    }

}

多线程的方法二(实现)

  1. 创建一个实现了Runnable接口的类
  2. 重写run();
  3. 创建实现Runnable类的对象;
  4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象;
  5. 通过Thread类的对象调用start();

    //创建一个实现了Runnable接口的类
    class mythread implements Runnable {
        private int tickect = 100;
    //重写run();
        @Override
        public void run() {
            while (tickect > 0) {
                System.out.println(Thread.currentThread().getName() + "卖了第" + tickect + "张票");
                tickect--;
            }
        }
    }
    
    
    
    public class App {
    
        public static void main(String[] args) throws InterruptedException {
            //创建实现类的对象;
            mythread mythread = new mythread();
            //将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象;
            Thread t1 = new Thread(mythread);
            Thread t2 = new Thread(mythread);
            Thread t3 = new Thread(mythread);
    
            t1.setName("一号窗口");
            t2.setName("二号窗口");
            t3.setName("三号窗口");
            t1.setPriority(Thread.MAX_PRIORITY);
            //通过Thread类的对象调用start();
            t1.start();
            t2.start();
            t3.start();
        }
    }

优势:实现的方式没有类的单继承的局限性

实现的方式更适合处理多个共享数据的类型

采用匿名内部类创建:

public class ThreadTest04 {
    public static void main(String[] args) {
        // 创建线程对象,采用匿名内部类方式。
        Thread mythread = new Thread(new Runnable(){
            @Override
            public void run() {
                for(int i = 0; i < 100; i++){
                    System.out.println("mythread线程---> " + i);
                }
            }
        });

        // 启动线程
        mythread.start();

        for(int i = 0; i < 100; i++){
            System.out.println("main线程---> " + i);
        }
    }
}

线程的安全问题

  1. 问题:买票过程中出现了重票错票
  2. 原因:当某个线程尚未操作完成时,其他线程参与过来,也操作车票

1同步代码块

public void run() {

        while (tickect > 0) {
            //
            synchronized (this){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName() + "卖了第" + tickect + "张票");
            tickect--;
        }
    }
}



public class App {

    public static void main(String[] args) throws InterruptedException {
        mythread mythread = new mythread();
        Thread t1 = new Thread(mythread);
        Thread t2 = new Thread(mythread);
        Thread t3 = new Thread(mythread);

        t1.setName("一号窗口");
        t2.setName("二号窗口");
        t3.setName("三号窗口");

        t1.start();
        t2.start();
        t3.start();
    }
}

关键字synchronized(同步监视器)

  1. 操作共享数据的代码就是需要同步的代码
  2. 同步监视器俗称锁,任何一个类的对象都可以充当锁
  3. 要求:需要同步的线程要共用同一把锁
  4. 操作同步代码时只能运行一个线程,相当于一个单线程,但能保证安全问题

实现方法的synchronized

class mythread implements Runnable {
    private int tickect = 100;
    Object object=new Object();
    /*如果是继承方法的多线程,要这样书写
    Object static object=new Object();
    因为在主函数中创建了三个mythread对象,要加static只生成一个object确保公用一把锁。*/

    @Override
    public void run() {


    
        while (true) {
            synchronized (object) {
   //也可以直接写成synchronized (this)这里的this指myehread对象
                if (tickect > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println(Thread.currentThread().getName() + "卖了第" + tickect + "张票");
                    tickect--;
                } else {
                    break;
                }
            }


        }
    }

}



public class App {

    public static void main(String[] args) throws InterruptedException {
        mythread mythread = new mythread();
        Thread t1 = new Thread(mythread);
        Thread t2 = new Thread(mythread);
        Thread t3 = new Thread(mythread);

        t1.setName("一号窗口");
        t2.setName("二号窗口");
        t3.setName("三号窗口");

        t1.start();
        t2.start();
        t3.start();
    }
}

补充:在实现Runnable接口创建多线程的方式中,可以考虑用this充当对象

​ 在继承thread创建多线程的方式中,慎用this充当对象,可以考虑用当前类充当对象

2同步方法

如果操作共享数据的代码在一个方法中

  1. 实现接口多线程使用同步方法
class mythread implements Runnable {
    private int tickect = 100;


    @Override
    public void run() {


        boolean ture = true;
        while (ture) {
                show();
        }
    }
    
    //实现接口多线程使用同步方法
    public synchronized void show(){
        if (tickect > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName() + "卖了第" + tickect + "张票");
            tickect--;
        }
    }
}
  1. 继承thread多线程同步方法需要调用静态方法

    public static synchronized void show(){}

总结:
  1. 同步方法依然涉及到同步监视器,只是不显示说明。
  2. 非静态同步方法的同步监视器是:this

​ 静态同步方法的同步监视器是:当前类

3Lock锁

class mythread implements Runnable {
    private int tickect = 100;
    //实例化
    private ReentrantLock lock=new ReentrantLock();


    @Override
    public void run() {



        while (true) {
            
            //try/finally语句包含同步代码
            try {
                lock.lock();//lock锁
                if (tickect > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println(Thread.currentThread().getName() + "卖了第" + tickect + "张票");
                    tickect--;
                }else {
                    break;
                }
            }finally {
                //finally解锁
                lock.unlock();
            }

        }
    }
}
lock与synchronized的区别

synchronized在执行完同步代码后,自动释放同步监视器。

lock需要手动启动同步(lock),同时结束同步也需要手动实现(unlock)

死锁

public class sisuo {
    public static void main(String[] args) {
        StringBuffer s1 = new StringBuffer();
        StringBuffer s2 = new StringBuffer();

        new Thread(new Runnable()){
            @Override
            public void run() {
                synchronized (s1){
                    s1.append("a");
                    s2.append(1);

           //拿到s1锁进入阻断状态等待s2锁
                 
                    try {
                        sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }

                    synchronized (s2){
                        s1.append("b");
                        s2.append(2);
                    }
                }
                System.out.println(s1);
                System.out.println(s2);
            }
        }.start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (s2){
                    s1.append("c");
                    s2.append(3);

            //拿到s2锁进入阻断状态等待s1锁
                    try {
                        sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }

                    synchronized (s1){
                        s1.append("d");
                        s2.append(4);
                    }
                }
                System.out.println(s1);
                System.out.println(s2);
            }
        }) .start();
    }
}

练习

银行有一个账户.

有两个储户分别向同一个账户存3000元,每次存1000,存三次.每次存完打印账户余额.

class Account{
    //一个账户
    private double balance;

    public Account(double balance) {
        this.balance = balance;
    }

    public synchronized void deposit(double money)  {
        balance+=money;
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName()+balance);
    }
}

class Customer extends Thread{
    //两个储户实现公用一个账户

    private Account att;
    //构造器
    public Customer(Account att) {
        this.att = att;
    }

    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            att.deposit(1000);
        }
    }
}

public class sisuo {
    public static void main(String[] args) {
        Account account = new Account(0);
        Customer c1 = new Customer(account);
        Customer c2 = new Customer(account);

        c1.setName("账户1  ");
        c2.setName("账户2  ");


        c1.start();
        c2.start();
    }
}

多线程通讯

例子:使用两个线程交替打印1-100

涉及到的三个方法:

wait():一旦执行此方法,当前线程进入阻塞状态,并释放同步监视器

notify():会唤醒被wait的一个线程,如果有多个线程wait则比较优先级

notifyAll():唤醒所有被wait的线程

注意:

  1. 上述三个方法必须在同步代码块或同步方法中使用
  2. 上述方法默认为this.方法,要与synchronized保持一致
class num implements Runnable{
    private int num=1;
    @Override
    public void run() {

        synchronized (this){
            while (true){
                notify();
                if(num<=100){
                    System.out.println(Thread.currentThread().getName()+":"+num);
                    num++;
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }else {
                    break;
                }
            }
        }
    }
}

不懂的地方:上面代码的wait()与notify()放在循环外无法实现通讯

sleep与wait的异同

相同点:都进入阻塞状态

不同点:1.声明的位置不同:Thread类中声明的sleep,wait在object类中声明

​ 2.调用要求不同:sleep可以在任何场景下调用,wait必须的同步代码中调用

​ 3.sleep不会释放同步监视器(锁),wait会释放同步监视器(锁)

线程的创建方法三(Callable)

优点:1. 相比于run方法可以有返回值

​ 2.可以抛出异常

​ 3.支持泛型

操作步骤:

  1. 创建一个实现Callable的实现类
  2. 实现call方法,将操作声明在call方法中
  3. 创建Callable实现类的对象
  4. 将此实现类的对象作为参数,传递到FutureTask构造器,创建FutureTask对象
  5. 将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象
//1.创建一个实现Callable的实现类
class call implements Callable{
    int sum = 0;
    @Override
    //2.实现call方法,将操作声明在call方法中
    public Object call() throws Exception {
        for (int i = 0; i <= 100; i++) {
            System.out.println(i);
            sum+=i;
        }
        return sum;
    }
}
public  class text01 {
    public static void main(String[] args) {
        //3.创建Callable实现类的对象
        call call=new call();
        //4,将此实现类的对象作为参数,传递到FutureTask构造器,创建FutureTask对象
        FutureTask futureTask=new FutureTask(call);
       //5. 将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象
        new Thread(futureTask).start();

        try {
            Object sum = futureTask.get();
            System.out.println("总计"+sum);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }
    }

}

线程的创建方法四(线程池)

好处:

  1. 提高响应速度
  2. 降低资源消耗
  3. 便于线程管理
  • corePoolSize:核心池的大小
  • maximumPoolSize:最大线程数
  • keepAliveTime:线程没有任务时最多保存多长时间后终止

实现方法:

  1. 提供指定线程数量的线程池
  2. 执行指定的线程操作,需要实现Runnable接口或者Callable接口实现类的对象
  3. 将实例化的对象作为参数 执行service.execute();
  4. 关闭线程池
class thread implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            if(i%2==0){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }

        }
    }
}

class thread0 implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            if(i%2!=0){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
}

public class pool {
    public static void main(String[] args) {
        //1提供指定线程数量的线程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        //2执行指定的线程操作,需要实现Runnable接口或者Callable接口实现类的对象
        thread thread1 = new thread();
        thread0 thread2 = new thread0();
        //3将实例化的对象作为参数 执行service.execute();
        service.execute(thread1);
        service.execute(thread2);
        // service.submit();适用于callable

        System.out.println(service.getClass());
        ThreadPoolExecutor service1=(ThreadPoolExecutor)service;
        service1.setCorePoolSize(15);
        //4关闭线程池
        service.shutdown();
    }
}
🎉文章到这里就结束了,感谢诸佬的阅读。🎉

💕欢迎诸佬对文章加以指正,也望诸佬不吝点赞、评论、收藏加关注呀😘
在这里插入图片描述

相关文章
|
2月前
|
Java 数据安全/隐私保护
快手小红书抖音留痕工具,自动留痕插件工具,java代码开源
这个框架包含三个核心模块:主操作类处理点赞评论、配置管理类和代理管理类。使用时需要配合
|
1月前
|
算法 IDE Java
Java 项目实战之实际代码实现与测试调试全过程详解
本文详细讲解了Java项目的实战开发流程,涵盖项目创建、代码实现(如计算器与汉诺塔问题)、单元测试(使用JUnit)及调试技巧(如断点调试与异常排查),帮助开发者掌握从编码到测试调试的完整技能,提升Java开发实战能力。
182 0
|
2月前
|
Java 编译器 数据库连接
Java异常处理:写出更健壮的代码
Java异常处理:写出更健壮的代码
145 0
|
2月前
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
106 0
|
2月前
|
Java API 调度
从阻塞到畅通:Java虚拟线程开启并发新纪元
从阻塞到畅通:Java虚拟线程开启并发新纪元
269 83
|
2月前
|
存储 Java 调度
Java虚拟线程:轻量级并发的革命性突破
Java虚拟线程:轻量级并发的革命性突破
212 83
|
2月前
|
安全 Java 测试技术
Java 项目实战中现代技术栈下代码实现与测试调试的完整流程
本文介绍基于Java 17和Spring技术栈的现代化项目开发实践。项目采用Gradle构建工具,实现模块化DDD分层架构,结合Spring WebFlux开发响应式API,并应用Record、Sealed Class等新特性。测试策略涵盖JUnit单元测试和Testcontainers集成测试,通过JFR和OpenTelemetry实现性能监控。部署阶段采用Docker容器化和Kubernetes编排,同时展示异步处理和反应式编程的性能优化。整套方案体现了现代Java开发的最佳实践,包括代码实现、测试调试
89 0
|
2月前
|
SQL Java 数据库连接
Java 期末考试救急必备涵盖绝大多数核心考点及五大类经典代码助你过关
本文为Java期末考试复习指南,涵盖基础语法、面向对象编程、异常处理、文件操作、数据库连接五大核心考点,提供详细解析与实用代码示例,助力快速掌握重点,高效备考,轻松应对考试。
59 0