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();
    }
}
🎉文章到这里就结束了,感谢诸佬的阅读。🎉

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

相关文章
|
1天前
|
消息中间件 Java 应用服务中间件
JVM实战—1.Java代码的运行原理
本文介绍了Java代码的运行机制、JVM类加载机制、JVM内存区域及其作用、垃圾回收机制,并汇总了一些常见问题。
JVM实战—1.Java代码的运行原理
|
5天前
|
存储 网络协议 安全
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
**项目介绍**:本项目实现了一个基于TCP协议的C/S架构控制台聊天室,支持局域网内多客户端同时聊天。用户需注册并登录,用户名唯一,密码格式为字母开头加纯数字。登录后可实时聊天,服务端负责验证用户信息并转发消息。 **项目亮点**: - **C/S架构**:客户端与服务端通过TCP连接通信。 - **多线程**:采用多线程处理多个客户端的并发请求,确保实时交互。 - **IO流**:使用BufferedReader和BufferedWriter进行数据传输,确保高效稳定的通信。 - **线程安全**:通过同步代码块和锁机制保证共享数据的安全性。
55 23
|
9天前
|
传感器 监控 Java
Java代码结构解析:类、方法、主函数(1分钟解剖室)
### Java代码结构简介 掌握Java代码结构如同拥有程序世界的建筑蓝图,类、方法和主函数构成“黄金三角”。类是独立的容器,承载成员变量和方法;方法实现特定功能,参数控制输入环境;主函数是程序入口。常见错误包括类名与文件名不匹配、忘记static修饰符和花括号未闭合。通过实战案例学习电商系统、游戏角色控制和物联网设备监控,理解类的作用、方法类型和主函数任务,避免典型错误,逐步提升编程能力。 **脑图速记法**:类如太空站,方法即舱段;main是发射台,static不能换;文件名对仗,括号要成双;参数是坐标,void不返航。
34 5
|
12天前
|
Java 调度
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
当我们创建一个`ThreadPoolExecutor`的时候,你是否会好奇🤔,它到底发生了什么?比如:我传的拒绝策略、线程工厂是啥时候被使用的? 核心线程数是个啥?最大线程数和它又有什么关系?线程池,它是怎么调度,我们传入的线程?...不要着急,小手手点上关注、点赞、收藏。主播马上从源码的角度带你们探索神秘线程池的世界...
81 0
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
|
16天前
|
存储 监控 Java
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
142 60
【Java并发】【线程池】带你从0-1入门线程池
|
27天前
|
Java 程序员 开发者
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
105 14
|
1月前
|
安全 Java 程序员
Java 面试必问!线程构造方法和静态块的执行线程到底是谁?
大家好,我是小米。今天聊聊Java多线程面试题:线程类的构造方法和静态块是由哪个线程调用的?构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节有助于掌握Java多线程机制。下期再见! 简介: 本文通过一个常见的Java多线程面试题,详细讲解了线程类的构造方法和静态块是由哪个线程调用的。构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节对掌握Java多线程编程至关重要。
57 13
|
1月前
|
JavaScript NoSQL Java
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
184 96
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
|
1月前
|
安全 Java 开发者
【JAVA】封装多线程原理
Java 中的多线程封装旨在简化使用、提高安全性和增强可维护性。通过抽象和隐藏底层细节,提供简洁接口。常见封装方式包括基于 Runnable 和 Callable 接口的任务封装,以及线程池的封装。Runnable 适用于无返回值任务,Callable 支持有返回值任务。线程池(如 ExecutorService)则用于管理和复用线程,减少性能开销。示例代码展示了如何实现这些封装,使多线程编程更加高效和安全。
|
1月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
本文详细介绍了如何在Linux中通过在业务线程中注册和处理信号。我们讨论了信号的基本概念,并通过完整的代码示例展示了在业务线程中注册和处理信号的方法。通过正确地使用信号处理机制,可以提高程序的健壮性和响应能力。希望本文能帮助您更好地理解和应用Linux信号处理,提高开发效率和代码质量。
49 17

热门文章

最新文章