java-----多线程

多线程基本概念

1.什么是多线程?

有了多线程,我们就可以让程序同时做多件事情

2.多线程的作用?

提高效率,让cpu在多个程序之间切换

3.多线程的应用场景?

只要你想让多个事情同时运行就需要多线程

比如:软件中的耗时操作、所有聊天软件、所有的服务器

并发和并行

1.并发:在同一时刻,有多个指令在单个CPU上交替执行

2.并行:在同一时刻,有多个指令在多个CPU上同时执行

多线程三种实现方式

1.继承Thread类的方式实现

将一个类声明为Thread的子类。 这个子类应该重写Thread类的方法run。

代码块演示:

复制代码
public class MyThread extends Thread{
    //子类调用父类的getName方法
    @Override
    public void run() {//t1 t2都会执行当前
        //线程要执行的
        for (int i = 0; i < 20; i++) {
            Thread thread = Thread.currentThread();
            System.out.println(getName()+"helloworld");
        }
    }
}

测试类main方法:

复制代码
public class demo1{
    public static void main(String[] args) {
        //1.自己定义一个类继承Thread
        //2.重写run方法
        //3.创建子类对象,并启动线程

        MyThread t1=new MyThread();
        MyThread t2=new MyThread();

        t1.setName("线程1");
        t2.setName("线程2");
        //启动线程
        t1.start();
        t2.start();

    }
}

运行结果:仅仅是一部分运行结果,可以看出线程1和2是交替执行的

2.实现Runnable接口的方式进行实现

创建一个线程是声明实现类Runnable接口。 那个类然后实现了run方法。 然后可以分配类的实例,在创建Thread时作为参数传递,并启动。

代码演示:

复制代码
public class MyRun implements Runnable{
    //自强
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            Thread thread = Thread.currentThread();//获取当前线程
            System.out.println(thread.getName()+"nihao");
        }
    }
}
复制代码
public class demo2 {
    public static void main(String[] args) {
        //1.自己定义一个类实现Runnable接口
        //2.重写里面的run方法
        //3.创建自己类的对象
        //4.创建一个Thread对象,并开启线程
        MyRun mr=new MyRun();
        Thread t1=new Thread(mr);
        Thread t2=new Thread(mr);
        t1.setName("线程1");
        t1.start();

        t2.setName("线程2");
        t2.start();
    }
}

3.利用Callable接口和Future接口方式实现

可以获得多线程执行的返回结果

复制代码
import java.util.concurrent.Callable;

public class MyCallable implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        int sum=0;
        for (int i = 0; i <=100; i++) {
            sum+=i;
        }
        return sum;
    }
}
复制代码
public class demo3 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //可以获取多线程运行的返回结果
        //1.创建MyCallable对象 表示多线程要执行人物
        MyCallable mc=new MyCallable();
        //创建FutureTask 管理结果
        FutureTask<Integer> ft=new FutureTask<>(mc);
        Thread t1=new Thread(ft);
        t1.start();
        Integer result=ft.get();
        System.out.println(result);
    }
}

运行结果:

5050

多线程中常见成员方法

部分代码演示:

线程优先级:

抢占式调度:随机性、优先级越大,线程抢占概率越大

复制代码
public class demo11 {
    public static void main(String[] args) {
        MyThread4 t1=new MyThread4("坦克");
        MyThread4 t2=new MyThread4("飞机");
        System.out.println(t1.getPriority());
        System.out.println(t2.getPriority());
        t1.setPriority(1);
        t2.setPriority(10);//最大优先级为10
        t1.start();
        t2.start();
    }
}
public class MyThread4 extends Thread{
    public MyThread4(String name){
        super(name);
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName()+"-"+i);
        }
    }
}
执行结果:
飞机和坦克都可能先执行完毕,优先级不是绝对只是概率问题

守护线程:

当非守护线程执行完毕后,守护线程就会结束。

应用场景:

比如qq两天,其中一个要发送文件给另一个人,聊天是线程1,传输文件时线程2,当线程1执行结束后,那么线程2也没有存在的必要了,线程2可以设置为守护线程。

线程生命周期

线程安全问题

应用场景:

某电影院目前正在上映国产大片,共有100张票,而它又3个窗口买票,设计一个程序模拟电影院买票。

复制代码
public class demo7 {
    public static void main(String[] args) {
        ThreadMyRun tmr=new ThreadMyRun();
        Thread t1=new Thread(tmr);
        Thread t2=new Thread(tmr);
        Thread t3=new Thread(tmr);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

运行结果:会发现出现重复的票,也会有超出范围的票

原因:

1.打印会出现重复的票:

三个线程,第一个线程执行完之后,会睡眠10毫秒,然后线程2这时候会抢占进程,然后睡眠10毫秒,然后第三个线程执行完之后,会睡眠10毫秒,一直在ticket++,最后都输出卖出ticket张票,所以这里出现了打印出现重复的票.

2.超出范围的票

当打印到99的时候,三个线程出现上面的情况,因为一直没有打印完,ticket++了三次,所以打印完之后才会出现再去判断票是否还满足100以内.打印了102、102、102

怎么解决这个问题?

当有线程在代码中执行时,就把它锁起来,只有当前线程执行完之后,其它线程才能进入,把共享数据的这段代码锁起来

同步代码块

把操作共享数据的代码锁起来

格式:

synchronized(锁){

操作共享数据的代码。

}

特点1:锁默认打开,有一个线程进去了,锁自动关闭

特点2:里面的代码全部执行完毕,线程出来,锁自动打开

复制代码
public class ThreadMyRun implements Runnable{
    int ticket=0;//这种创建方法 因为ThreadMyRun是传递的参数 只会创建一次
    @Override
    public void run() {
        while(true){
            try {
                if (method()) break;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    private boolean method() throws InterruptedException {
        synchronized (ThreadMyRun.class){ //1.不能写在while循环的外面,因为如果放在外面,只有第一个抢到运行优先权线程执行完while之后,才能第二个线程进入同步代码块
2.同步代码块里的锁必须时唯一的,这里用的字节码文件,如果用几个线程用不同的锁,那么锁的意义也就没了
            if(ticket==100){
                return true;
            }
            else{
                Thread.sleep(100);
                ticket++;
                System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"票");
            }
        }
        return false;
    }
}

运行结果:可以看到正常运行

lock锁

提供了获得锁和释放锁的方法:lock()、unlock()

复制代码
public class LockDemo extends Thread{
    static int ticket=0;
    static Lock lock=new ReentrantLock();//因为Lock是接口,需要创建实现类对象
    @Override
    public void run() {
        lock.lock();//加锁
        while(true){
            if(ticket==100){
                break;
            }
            else{
                try {
                    Thread.sleep(1000);
                    ticket++;
                    System.out.println(getName()+"正在卖第"+ticket+"张票");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();//执行完毕之后释放锁
                }
            }
        }
    }
}

死锁

死锁是一种常见的问题,它发生在多个线程相互等待对方释放资源时,导致它们都无法继续执行。

一般锁的嵌套会出现这种问题。

生产者消费者问题

代码模拟:

复制代码
public class Cook extends Thread{
    @Override
    public void run() {
        while(true){
            synchronized (Desk.lock){
                if(Desk.count==0){
                    break;
                }
                else{
                    if(Desk.foodFlag==1){
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    else{
                        System.out.println("厨师又做了一碗,还有");
                        Desk.foodFlag=1;
                        Desk.lock.notifyAll();//等待消费者
                    }
                }
            }
        }
    }
}
复制代码
public class Foodie extends Thread{
    @Override
    public void run() {
        /*
        * 1.循环
        * 2.同步代码块
        * 3.共享数据是否到了末尾 到了末尾
        * 4.未到末尾
        * */
        while(true){
            synchronized (Desk.lock){
                if(Desk.count==0){
                    break;
                }
                else{
                    if(Desk.foodFlag==0){//没有面条
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    else{
                        Desk.count--;
                        System.out.println("吃过正在吃面条,还能再吃"+Desk.count+"碗");
                        Desk.lock.notifyAll();//唤醒的是厨师
                        Desk.foodFlag=0;
                    }
                }
            }
        }
    }
}
复制代码
package ThreadDemo;

public class Desk {
    /*
    控制生产者消费者的执行
     */
    //是否有面条 0有 1没有
    public static int foodFlag=0;
    //总个数
    public static int count=10;
    //上锁
    public static Object lock=new Object();

}
复制代码
public class demo9 {
    public static void main(String[] args) {
        Cook c=new Cook();
        Foodie f=new Foodie();
        c.setName("厨师");
        c.setName("吃货");

        c.start();
        f.start();
    }
}
运行结果:

生产者消费者阻塞队列实现

复制代码
public class Cook2 extends Thread{
    ArrayBlockingQueue<String> queue;
    public Cook2(ArrayBlockingQueue<String>queue){
        this.queue=queue;
    }
    @Override
    public void run() {
        while(true){
            try {
                queue.put("面条");
                System.out.println("厨师放一碗面条");//这个是锁的外面
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}
复制代码
public class Food2 extends Thread{
    ArrayBlockingQueue<String> queue;
    public Food2(ArrayBlockingQueue<String>queue){
        this.queue=queue;
    }
    @Override
    public void run() {
        while(true){
            try {
                String food=queue.take();
                System.out.println(food);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }


        }
    }
}
复制代码
public class demo10 {
    public static void main(String[] args) {
        ArrayBlockingQueue<String>queue=new ArrayBlockingQueue<>(1);//创建容量为1的阻塞队列

        Food2 food2=new Food2(queue);
        Cook2 cook2=new Cook2(queue);

        food2.start();
        cook2.start();
    }
}

线程6种状态

相关推荐
非优秀程序员12 小时前
第04章 邮箱大战,163 与 QQ 的暗战|《互联网产品恩仇录》
java·vue.js·架构
知已难寻12 小时前
Docker 入门:容器技术与 Docker 基础
java·运维·服务器·docker·云原生·容器
沐晓时光12 小时前
C语言入门,内存函数知识讲解
c语言·开发语言
文丑颜不良啊14 小时前
LeetCode2481~2500题目解析
java·leetcode
Flynn Liang15 小时前
同一需求三套写法:用 Spring AI、LangChain4j、AgentScope Java 各实现一个带工具调用的 RAG 助手(实战对比)
java·人工智能·大模型·架构设计·spring ai·langchain4j·agentscope
聪明的笨猪猪16 小时前
实际工程问题:AI 生成 Spring Cache(@Cacheable / @CachePut / @CacheEvict)缓存注解踩坑
java·经验分享·面试·ai编程
愿有所息16 小时前
记一次 ARM64 + JDK8 上 fastjson2 的诡异数据错乱
java
秋饼16 小时前
MCP 2026-07-28 无状态协议大改版:Spring AI 2.1 多轮往返请求与网关路由落地
java·ai·技术分享·后端开发
SL_staff18 小时前
JVS-BI 实战:业务人员如何用零代码 ELT + 可视化配置构建实时经营分析闭环
java·spring·全栈
SL_staff18 小时前
企业AI落地难?JVS-AI渐进式实践:安全可控、小切口验证、标准化沉淀与规模化复制
java·开源·aigc