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

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

相关文章
|
6天前
|
Java
在 Java 中捕获和处理自定义异常的代码示例
本文提供了一个 Java 代码示例,展示了如何捕获和处理自定义异常。通过创建自定义异常类并使用 try-catch 语句,可以更灵活地处理程序中的错误情况。
|
6天前
|
安全 Java 开发者
深入解读JAVA多线程:wait()、notify()、notifyAll()的奥秘
在Java多线程编程中,`wait()`、`notify()`和`notifyAll()`方法是实现线程间通信和同步的关键机制。这些方法定义在`java.lang.Object`类中,每个Java对象都可以作为线程间通信的媒介。本文将详细解析这三个方法的使用方法和最佳实践,帮助开发者更高效地进行多线程编程。 示例代码展示了如何在同步方法中使用这些方法,确保线程安全和高效的通信。
25 9
|
9天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####
|
6天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
9天前
|
Java
JAVA多线程通信:为何wait()与notify()如此重要?
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是实现线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件满足时被唤醒,从而确保数据一致性和同步。相比其他通信方式,如忙等待,这些方法更高效灵活。 示例代码展示了如何在生产者-消费者模型中使用这些方法实现线程间的协调和同步。
24 3
|
8天前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
9天前
|
Java
java小知识—进程和线程
进程 进程是程序的一次执行过程,是系统运行的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统资源如CPU时间,内存空间,文件,文件,输入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。 线程 线程,与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间做切换工作时,负担要比
20 1
|
9天前
|
Java UED
Java中的多线程编程基础与实践
【10月更文挑战第35天】在Java的世界中,多线程是提升应用性能和响应性的利器。本文将深入浅出地介绍如何在Java中创建和管理线程,以及如何利用同步机制确保数据一致性。我们将从简单的“Hello, World!”线程示例出发,逐步探索线程池的高效使用,并讨论常见的多线程问题。无论你是Java新手还是希望深化理解,这篇文章都将为你打开多线程的大门。
|
3月前
|
存储 监控 Java
Java多线程优化:提高线程池性能的技巧与实践
Java多线程优化:提高线程池性能的技巧与实践
115 1
|
6月前
|
设计模式 监控 Java
Java多线程基础-11:工厂模式及代码案例之线程池(一)
本文介绍了Java并发框架中的线程池工具,特别是`java.util.concurrent`包中的`Executors`和`ThreadPoolExecutor`类。线程池通过预先创建并管理一组线程,可以提高多线程任务的效率和响应速度,减少线程创建和销毁的开销。
197 2