java——多线程

本文介绍了进程和线程的概念,指出线程是进程的实体,一个进程可有多线程。阐述了进程和线程在地址空间、资源拥有等方面的区别,还涉及调度、同步、并发并行等问题。此外,讲解了线程的安全问题及解决方案,如同步代码块、同步方法等,最后提及线程池和Lambda表达式等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.什么是进程

狭义:进程是正在运行的程序实例

广义:进程是一个具有独立功能的程序关于某个数据集合的一次运行活动,是系统进行资源分配和调度的基本单位

进程的概念要注意两点:

进程是一个实体。每一个进程都有它自己的独立地址空间
进程是一个“执行中的程序”。只有当处理器执行这个程序时,它成为活动的实体
2.什么是线程

   线程是进程中的一个实体,是CPU调度的基本单位,只拥有在运行中必不可少的资源,但它可与同属一个进程的其他线程共享所拥有的资源。

3.线程和进程的关系

线程是进程的一个实体,一个进程可以拥有多个线程。

4.进程和线程的区别

(1) 地址空间:同一进程的所有线程共享本进程的地址空间,不同进程之间的地址空间是相互独立的。
(2) 资源拥有:同一进程内的线程共享本进程的资源如:内存、I/O,CPU等,不同的进程资源相互独立。
(3) 健壮性:一个进程崩溃后,不会对其他进程产生影响,但是一个线程崩溃后,会终结整个进程。所以多进程要比多线程健壮。
(4) 线程切换比进程切换消耗的资源更少、速度更快。
(5) 线程是CPU调度的基本单位,进程是系统资源分配的基本单位。
调度问题
在这里插入图片描述
同步与异步
在这里插入图片描述
并发与并行
并发指两个或多个事件在同一个时间段内发生。
并行指两个或多个事件在同一时刻发生(同时发生)
开启两个线程,使用抢占式的执行方式
在这里插入图片描述
在这里插入图片描述
他们的执行方式是这样的
在这里插入图片描述
在这里插入图片描述
Runnable接口
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
设置和获取线程名称
在这里插入图片描述

public class setname {
    public static void main(String[] args) {
        //如何获取线程的名称
        System.out.println(Thread.currentThread().getName());
        //两种设置线程名称的方式
        Thread t = new Thread(new MyRunnable());
        t.setName("wwww");
        t.start();
        new Thread(new MyRunnable(),"锄禾日当午").start();
        //不设置的有默认的名字
        new Thread(new MyRunnable()).start();
    }
    static class MyRunnable implements Runnable{

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
    }
}

下边的线程不是固定的,线程是抢占执行的
顺序不是固定的是


import  java.lang.Thread;
public class THread {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName());
        //两种方式去创建线程
        MyRunnable mr = new MyRunnable();
        Thread t = new Thread(mr);
        t.start();
        new Thread(new MyRunnable()).start();
    }
}
 class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

在这里插入图片描述
指定时间休眠Sleep

public class setname {
    public static void main(String[] args) {
      new Thread(new MyRunnable()).start();
    }
    static class MyRunnable implements Runnable{

        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("已经休眠完成");
        }
    }
}

在这里插入图片描述
线程阻塞就是所有消耗时间的操作。如果用stop直接结束线程,那么它没办法及时的把它所拥有的资源去释放
线程的中断(中断只是一个标记,告诉线程该死亡了,然后在线程中添加死亡的方法)

import sun.management.ThreadInfoCompositeData;

public class setname {
    public static void main(String[] args) {
     MyRunnable r1=new MyRunnable();
     Thread t1=new Thread(r1);
      t1.start();
      t1.interrupt();
    }
    static class MyRunnable implements Runnable{

        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                //e.printStackTrace();
                System.out.println("发现了中断标记该结束了");
                return;
            }
            System.out.println("已经休眠完成");
        }
    }
}

在这里插入图片描述
在实际编程中一般不用stop,stop会强制关闭,如果当时该线程没有释放所拥有的资源会造成麻烦

守护线程
用户线程:当一个进程不包含任何的存活用户线程时,进行结束
守护线程: 守护用户线程的,当最后一个用户线程结束时,所有守护线程自 动死亡

import sun.management.ThreadInfoCompositeData;

public class setname {
    public static void main(String[] args) throws InterruptedException {
     MyRunnable r1=new MyRunnable();
     Thread t1=new Thread(r1);
     //设置t1为守护线程,在本例中只有main一个用户线程
      t1.setDaemon(true);
      t1.start();
     for(int i=1;i<5;i++){
         System.out.println(Thread.currentThread().getName()+":"+i);
         Thread.sleep(1000);
     }
    }
    static class MyRunnable implements Runnable{

        @Override
        public void run() {
            for(int i=0;i<10;i++){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    //e.printStackTrace();
                    System.out.println("发现了中断标记该结束了");
                    return;
                }

                System.out.println(Thread.currentThread().getName()+":"+i);
            }

        }
    }
}

在本例中,守护线程应该执行10次,但是当用户线程main执行完成后,就结束了在这里插入图片描述
线程的安全与不安全

public class setname {
    public static void main(String[] args) {
        //线程不安全
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable{
        //总票数
        private int count = 10;
        @Override
        public void run() {
            while (count>0){
                //卖票
                System.out.println("正在准备卖票");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count--;
                System.out.println("卖票结束,余票:"+count);
            }
        }
    }
}

在这里插入图片描述
当余票剩余1 的情况下,A进入catch但是此时丢失了时间片,被B拿到,此时count的值还没来得及改变于是会发生-1张票的情况
在这里插入图片描述
线程安全的解决方案一——同步代码块(上锁 ——synchronized,但是上完锁以后效率就变得不高了,谁先抢到,谁更容易抢到接下来的)
三个线程看同一把锁 Object O

//线程同步synchronized

public class setname {
    public static void main(String[] args) {
        Object o = new Object();
        //线程不安全
        //解决方案1  同步代码块
        //格式:synchronized(锁对象){
        //
        //
        //      }
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable{
        //总票数
        private int count = 10;
        private Object o = new Object();
        @Override
        public void run() {
            //Object o = new Object();    //这里不是同一把锁,所以锁不住
            while (true) {
                synchronized (o) {
                    if (count > 0) {
                        //卖票
                        System.out.println("正在准备卖票");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        count--;
                        System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
                    }else {
                        break;
                    }

                }
            }
        }
    }
}

可以看到,方法一一旦哪个线程抢到了,那么接下来就会执行很方便
在这里插入图片描述
反例,如果Object O放在run里边,那么三个线程在创建的时候都看各自的锁,此时就是线程不安全的
在这里插入图片描述
方法二 同步方法

//线程同步synchronized

public class setname {
    public static void main(String[] args) {
        Object o = new Object();
        //线程不安全
        //解决方案2  同步方法
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }
    static class Ticket implements Runnable{
        //总票数
        private int count = 10;
        @Override
        public void run() {
            while (true) {
                boolean flag = sale();
                if(!flag){
                    break;
                }
            }
        }
        public synchronized boolean sale(){
            if (count > 0) {
                //卖票
                System.out.println("正在准备卖票");
                try {
                    Thread.sleep(1500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count--;
                System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
                return true;
            }
            return false;

        }
    }
}

需要注意的是这里
在这里插入图片描述
如果里边new 三个Ticket
排队的对象是里边创建的这个Ticket,现在里边新创建的,三个,会调用三个run,没有实现排队机制,而之前里边放的是run,三个线程使用他会被锁,实现排队机制,但是上面这种方式不行

方法三——显示锁
一定注意锁的参数,如果是false,那么很大可能被某一个线程独占资源,其他线程得不到资源的分配

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//同步代码块和同步方法都属于隐式锁
//线程同步lock

public class setname {
    public static void main(String[] args) {
        //线程不安全
        //解决方案1   显示锁  Lock  子类 ReentrantLock
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable{
        //总票数
        private int count = 10;
        //参数为true表示公平锁    默认是false 不是公平锁
        private Lock l = new ReentrantLock(true);
        @Override
        public void run() {
            while (true) {
                l.lock();
                if (count > 0) {
                    //卖票
                    System.out.println("正在准备卖票");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count--;
                    System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
                }else {
                    break;
                }
                l.unlock();
            }
        }
    }
}

显示锁和隐式锁的区别:
https://blog.csdn.net/ZL_do_it/article/details/106059939?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160422284619195264700274%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=160422284619195264700274&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_v2~rank_v28-1-106059939.first_rank_ecpm_v3_pc_rank_v2&utm_term=%E6%98%BE%E7%A4%BA%E9%94%81%E5%92%8C%E9%9A%90%E5%BC%8F%E9%94%81%E7%9A%84%E5%8C%BA%E5%88%AB&spm=1018.2118.3001.4449

公平锁:谁先到谁得到锁
非公平锁:大家一块抢
在这里插入图片描述
在这里插入图片描述
上边是唤醒所有线程,下边是让当前线程睡着。
多线程通信——生产者消费者问题,就像是厨师和服务员一样,当厨师在工作的时候,服务员线程在wait(沉睡),当服务员送餐的时候厨师在(wait)

public class setname {
    public static void main(String[] args) {
        //多线程通信    生产者与消费者问题
        Food f = new Food();
        new Cook(f).start();
        new Waiter(f).start();
    }
    //厨师
    static class Cook extends Thread{
        private Food f;

        public Cook(Food f) {
            this.f = f;
        }

        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                if(i%2==0){
                    f.setNameAndTaste("老干妈小米粥","香辣味");
                }else {
                    f.setNameAndTaste("煎饼果子","甜辣味");
                }
            }
        }
    }
    //服务员
    static class Waiter extends Thread{
        private Food f;

        public Waiter(Food f) {
            this.f = f;
        }
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                f.get();
            }
        }
    }
    //食物
    static class Food{
        private String name;
        private String taste;
        //true表示可以生产
        boolean flag = true;
        public synchronized void setNameAndTaste(String name,String taste){
            if(flag){
                this.name = name;
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.taste = taste;
                flag = false;
                this.notifyAll();
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
        public synchronized void get(){
            if(!flag){
                System.out.println("服务员端走的菜的名称是:"+name+",味道是:"+taste);
                flag = true;
                this.notifyAll();
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

线程的状态
在这里插入图片描述
线程的状态转换
在这里插入图片描述
Java中第三种线程Callable实现方式(作为了解)
在这里插入图片描述
在这里插入图片描述
返回值会先打印下边的123456789,然后get方法等待子线程执行完成后再执行他自己的线程在这里插入图片描述
判断线程是否执行完在这里插入图片描述
取消线程在这里插入图片描述
线程池是容纳多个线程的容器(相当于一个线程数组)开发中很少使用线程池。(java中有四种线程池)
在这里插入图片描述
缓存线程池
向线程池中加入任务(指挥线程池执行新的任务)在这里插入图片描述
在这里插入图片描述
再复制那个方法,因为线程池中已经有了缓存,所以会调用在缓存中的
在这里插入图片描述
定长线程池在这里插入图片描述
单线程线程池在这里插入图片描述
执行完不会关闭还在等待传入,但是在一定时间后还会自动关闭的
周期线程池在这里插入图片描述
Lambda表达式
以前创建线程的方式在这里插入图片描述
匿名内部类的使用(比原来简便一些)
在这里插入图片描述
函数式编程
在这里插入图片描述
接口只有一个方法才可以使用lambda表达式在这里插入图片描述
修改后的在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

凌晨里的无聊人

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

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

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

打赏作者

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

抵扣说明:

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

余额充值