Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >java多线程

java多线程

作者头像
不期而遇丨
发布于 2022-09-09 10:00:56
发布于 2022-09-09 10:00:56
90800
代码可运行
举报
运行总次数:0
代码可运行

java多线程

进程与线程

进程:是程序的一次执行过程,或是正在运行的一个程序,是一个动态的过程,有它自身的产生,存在和消亡的过程。-------生命周期 线程:进程可进一步细化为线程,是一个程序内部的一条执行路径

一个进程可以有多个线程;

线程的五个状态
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1. 新建状态(New)         : 线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread()2. 就绪状态(Runnable)	 : 也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。
3. 运行状态(Running) 	 : 线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。
4. 阻塞状态(Blocked)  	 : 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
    	(01) 等待阻塞 -- 通过调用线程的wait()方法,让线程等待某工作的完成。
    	(02) 同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
    	(03) 其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()	等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5. 死亡状态(Dead)    	 : 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
创建线程的三种方式

继承 Thread 类、实现 Runnable 接口、实现Callable接口

继承Thread类实现多线程案例模拟
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//主线程
/*
*实现多线程,分别打印不同的数字
 */
public class Test {
    public static void main(String[] args) {
        //新建线程
        Thread test=new Test1();
        //启动线程
        test.start();
        //主线程执行 代码
        for (int i = 0; i < 100; i++) {
            System.out.println("主线程打印:"+i);
        }
    }
}

//多线程继承Thread类
class Test1 extends Thread{

    //重写run()方法
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //Thread.currentThread().getName()获得当前线程的名字
            System.out.println(Thread.currentThread().getName()+"线程打印:"+i);
        }
    }
}


//执行main方法后,得到的结果(仅复制部分结果,可以自己试一下)
/*
主线程打印:0
Thread-0线程打印:0
主线程打印:1
Thread-0线程打印:1
主线程打印:2
主线程打印:3
主线程打印:4
*/
实现Runnable接口实现多线程案例模拟

注意:Runnable也需要通过Thread类启动线程

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Test01 {
    //主线程
    public static void main(String[] args) {
        //新建线程
        Test2 test=new Test2();
        //启动线程:Runnable不能直接.start执行,也需要通过Thread类启动线程
        Thread t1=new Thread(test);
        t1.start();
        //主线程执行 代码
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"线程打印:"+i);
        }
    }
}

//多线程实现Runnable接口
class Test2 implements Runnable{

    //重写run()方法
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"线程打印:"+i);
        }
    }
}

//执行main方法后,得到的结果(仅复制部分结果,可以自己试一下)
/*
main线程打印:0
main线程打印:1
main线程打印:2
main线程打印:3
main线程打印:4
main线程打印:5
main线程打印:6
Thread-0线程打印:0
main线程打印:7
Thread-0线程打印:1
main线程打印:8
main线程打印:9
*/
实现Callable接口实现多线程

Callable和Runnable都是接口的形式实现多线程但是Callable可以有返回值,也可以抛出异常,与Thread和Runnable不同的是它并不是通过重写run()方法而是call()方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import java.util.concurrent.*;

public class Test02 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Test3 t3=new Test3();
        ExecutorService executorService= Executors.newSingleThreadExecutor();
        Future submit = executorService.submit(t3);


        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"线程打印"+i);
        }
        System.out.println(submit.get());
    }
}

class Test3 implements Callable {

    @Override
    public String call() throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"线程打印"+i);
        }

        return Thread.currentThread().getName()+"线程打印结束";
    }
}

//执行main方法后,得到的结果(仅复制部分结果,可以自己试一下)
/*
pool-1-thread-1线程打印99
main线程打印92
main线程打印93
main线程打印94
main线程打印95
main线程打印96
main线程打印97
main线程打印98
main线程打印99
pool-1-thread-1线程打印结束
*/
并发问题

多个线程操作同一个变量,存在线程不安全问题,数据紊乱

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Test03 {
    public static void main(String[] args) {
        Test4 test4 = new Test4();
        new Thread(test4,"小刘").start();
        new Thread(test4,"小网").start();
        new Thread(test4,"小赵").start();
    }
}

class Test4 implements Runnable{

    private int tick = 20;

    @Override
    public void run() {
        while (true){
            if(tick<=0){
                break;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"买了地"+tick--+"张票");
        }
    }
}
//运行结果:
/*
小网买了地19张票
小赵买了地18张票
小刘买了地20张票
小赵买了地17张票
小刘买了地16张票
小网买了地17张票
小网买了地15张票
小赵买了地14张票
小刘买了地13张票
小网买了地12张票
小赵买了地11张票
小刘买了地10张票
小赵买了地9张票
小刘买了地8张票
小网买了地9张票
小网买了地7张票
小刘买了地6张票
小赵买了地7张票
小赵买了地5张票
小网买了地4张票
小刘买了地5张票
小赵买了地3张票
小刘买了地2张票
小网买了地3张票
小网买了地1张票
小刘买了地0张票
小赵买了地1张票
*/
synchronized关键字
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1. synchronized原理
在java中,每一个对象有且仅有一个同步锁。这也意味着,同步锁是依赖于对象而存在。
当我们调用某对象的synchronized方法时,就获取了该对象的同步锁。例如,synchronized(obj)就获取了“obj这个对象”的同步锁。
不同线程对同步锁的访问是互斥的。也就是说,某时间点,对象的同步锁只能被一个线程获取到!通过同步锁,我们就能在多线程中,实现对“对象/方法”的互斥访问。 例如,现在有两个线程A和线程B,它们都会访问“对象obj的同步锁”。假设,在某一时刻,线程A获取到“obj的同步锁”并在执行一些操作;而此时,线程B也企图获取“obj的同步锁” —— 线程B会获取失败,它必须等待,直到线程A释放了“该对象的同步锁”之后线程B才能获取到“obj的同步锁”从而才可以运行。
2. synchronized基本规则
第一条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的该“synchronized方法”或者“synchronized代码块”的访问将被阻塞。
第二条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程仍然可以访问“该对象”的非同步代码块。
第三条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的其他的“synchronized方法”或者“synchronized代码块”的访问将被阻塞。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 @Override
    public synchronized void run() {
        while (true){
            if(tick<=0){
                break;
            }
                System.out.println(Thread.currentThread().getName()+"买了地"+tick--+"张票");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

//通过synchronized修饰方法,或者synchronized代码块的方式,使线程安全
 @Override
    public void run() {
        while (true){
            if(tick<=0){
                break;
            }
            synchronized (this){
                System.out.println(Thread.currentThread().getName()+"买了地"+tick--+"张票");
            }

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

//打印结果
/*
小刘买了地20张票
小赵买了地19张票
小网买了地18张票
小刘买了地17张票
小网买了地16张票
小赵买了地15张票
小网买了地14张票
小赵买了地13张票
小刘买了地12张票
小赵买了地11张票
小刘买了地10张票
小网买了地9张票
小赵买了地8张票
小刘买了地7张票
小网买了地6张票
小刘买了地5张票
小网买了地4张票
小赵买了地3张票
小刘买了地2张票
小网买了地1张票
*/
//注意:如果使用synchronized修饰run方法,其他线程访问该方法时就会被阻塞,所以该实例会只有第一个线程买票(建议自己试一下)

/*
死锁

同步可以保证资源共享操作的正确性,但是过多同步也会产生问题。例如,现在张三想要李四的画,李四想要张三的书,张三对李四说“把你的画给我,我就给你书”,李四也对张三说“把你的书给我,我就给你画”两个人互相等对方先行动,就这么干等没有结果,这实际上就是死锁的概念。

所谓死锁,就是两个线程都在等待对方先完成,造成程序的停滞,一般程序的死锁都是在程序运行时出现的。
*/
线程常用方法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
线程的强制运行
在线程操作中,可以使用 join() 方法让一个线程强制运行,线程强制运行期间,其他线程无法运行,必须等待此线程完成之后才可以继续执行。

线程的休眠
在程序中允许一个线程进行暂时的休眠,直接使用 Thread.sleep() 即可实现休眠。

中断线程
当一个线程运行时,另外一个线程可以直接通过interrupt()方法中断其运行状态。

线程的优先级
在 Java 的线程操作中,所有的线程在运行前都会保持在就绪状态,那么此时,哪个线程的优先级高,哪个线程就有可能会先被执行。线程.setPriority(1~10) ;Thread类中有三个定义好的常量

线程的礼让
在线程操作中,也可以使用 yield() 方法将一个线程的操作暂时让给其他线程执行

守护线程

线程分为用户线程守护线程

虚拟机必须确保用户线程执行完毕才行,虚拟机不用等待守护线程执行完毕;

可以用来声明日志,监控信息等用途;

声明守护线程的方式:线程.setDaemon(boolean);true为守护线程,也就是说这个线程里面是死循环也不会报错,当用户线程执行完毕,他也会结束执行;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Test {
    public static void main(String[] args) {
        //新建线程
        Thread test=new Test1();
        //启动线程
        test.setDaemon(true);
        test.start();
        //主线程执行 代码
        for (int i = 0; i < 100; i++) {
            System.out.println("主线程打印:"+i);
        }
    }
}

//多线程继承Thread类
class Test1 extends Thread{

    //重写run()方法
    @Override
    public void run() {

        while (true){
            System.out.println(Thread.currentThread().getName()+"线程打印:");
        }

    }
}

//部分结果
/*
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
主线程打印:94
主线程打印:95
主线程打印:96
主线程打印:97
主线程打印:98
主线程打印:99
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:

Process finished with exit code 0(运行结束)
*/

补充(ArrayList也是线程不安全的)可以网上了解一下

Lock(锁)

synchronized是隐式的同步锁;

还有一种显式的锁Lock对象锁;

ReentrantLock(可重入锁)类实现了Lock,可以显式加锁,释放锁;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Test03 {
    public static void main(String[] args) {
        Test4 test4 = new Test4();
        new Thread(test4,"小刘").start();
        new Thread(test4,"小网").start();
        new Thread(test4,"小赵").start();
    }
}

class Test4 implements Runnable{

    private volatile int tick = 10;

    private final ReentrantLock lock=new ReentrantLock();

    @Override
    public void run() {
        while (true){
                if(tick<=0){
                    break;
                }
                System.out.println(Thread.currentThread().getName()+"买了地"+tick--+"张票");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
        }
    }
}

//未加锁执行结果
/*
小网买了地10张票
小刘买了地8张票
小网买了地8张票
小赵买了地7张票
小赵买了地6张票
小网买了地4张票
小刘买了地5张票
小赵买了地2张票
小刘买了地1张票
小网买了地3张票
*/

//加锁后代码
class Test4 implements Runnable{

    private volatile int tick = 10;

    private final ReentrantLock lock=new ReentrantLock();

    @Override
    public void run() {
        while (true){
            try {
                //加锁
                lock.lock();
                if(tick<=0){
                    break;
                }
                System.out.println(Thread.currentThread().getName()+"买了地"+tick--+"张票");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }finally {
                //释放锁
                lock.unlock();
            }
        }
    }
}
//再执行
/*
小刘买了地10张票
小刘买了地9张票
小刘买了地8张票
小刘买了地7张票
小刘买了地6张票
小刘买了地5张票
小网买了地4张票
小网买了地3张票
小网买了地2张票
小网买了地1张票

*/
生产者消费者问题
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
生产者消费者问题是多线程的一个经典问题,描述成一块缓冲区作储存箱,生产者可以将产品放入储存箱,消费者则可以从储存箱中取走产品。

解决生产者/消费者问题的方法
采用某种机制保护生产者和消费者之间的同步
wait() / notify()方法(最高效)
wait() / nofity()方法是基类Object的两个方法:

wait()方法:当缓冲区已满/空时,生产者/消费者线程停止自己的执行,放弃锁,使自己处于等待状态,让其他线程执行。
notify()方法:当生产者/消费者向缓冲区放入/取出一个产品时,向其他等待的线程发出可执行的通知,同时放弃锁,使自己处于等待状态。
线程池

ExecutorService线程池接口

Executors工具类,线程池的工厂类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class TestPool {
    public static void main(String[] args) {
        //创建服务,创建线程池;参数是线程池大小
        ExecutorService service= Executors.newFixedThreadPool(10);

        //启动线程
        service.execute(new Test5());
        service.execute(new Test5());
        service.execute(new Test5());
        service.execute(new Test5());

        //关闭连接
        service.shutdown();
    }
}

class Test5 implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"打印了");
    }
}

//执行结果
/*
pool-1-thread-2打印了
pool-1-thread-4打印了
pool-1-thread-1打印了
pool-1-thread-3打印了
*/
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-06-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【Java多线程】的学习总结
多线程其实就是进程中一个独立的控制单元或者说是执行路径,线程控制着进程的执行,【重点】一个进程中,至少有一个线程存在。
张拭心 shixinzhang
2022/11/30
5770
JAVA多线程详解
分为两种:一类是守护线程,典型是垃圾回收GC;第二类是用户线程,当JVM中都是JVM守护线程,那么当前的JVM将退出。
阮键
2019/08/07
4290
java多线程超详细总结
第一步:定义Thread类的之类,并重写run方法,该run方法的方法体就代表了线程需要执行的任务
说故事的五公子
2019/09/11
1K0
java多线程超详细总结
Java多线程基础
主线程挂了但是子线程还在继续执行,这并不会导致应用程序的结束。说明: 当main线程启动一个子线程 Thread-0, 主线程不会阻塞, 会继续执行(不会等执行完毕后再往下执行),这时 主线程和子线程是交替执行。
timerring
2023/05/07
3040
Java多线程基础
Java多线程
在生活之中,我们常常可以一心多用。我可以一边打游戏,一边放着音乐听听歌,甚至可以再泡个脚。没错,这也可以理解成我的多线程生活。
半月无霜
2023/03/03
6420
Java多线程
Java多线程
例如打开你的计算机上的任务管理器,会显示出当前机器的所有进程,QQ,Chrome等,当QQ运行时,就有很多子任务在同时运行。比如,当你边打字发送表情,边好友视频时这些不同的功能都可以同时运行,其中每一项任务都可以理解成“线程”在工作。
用户10358987
2024/04/23
1100
Java多线程
java中的多线程
  线程是进程中一个小的执行单位,线程是不能脱离进称独立存在的,一个进程中可以有一个或多个线程。
别团等shy哥发育
2023/02/25
2.1K0
java中的多线程
Java多线程(一篇从0讲透)
进程:是指一个内存中运行的程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程
不吃紫菜
2023/04/02
3870
Java多线程(一篇从0讲透)
【day17】多线程基础
进程是在内存中执行的应用程序,而线程是进程中最小的执行单元,负责当前进程中程序的运行。一个进程中至少有一个线程,多个线程的应用程序称为多线程程序。
程序员波特
2024/12/26
890
【day17】多线程基础
☀️苏州程序大白一文解析Java多线程☀️《❤️记得收藏❤️》
程序:是为完成特定任务,用某种语言编写的一组指令的集合,即指一段静态的代码,静态对象。
苏州程序大白
2021/10/13
3590
Java多线程Thread VS Runnable详解
本文讲述如何使用Java多线程技术实现一个安全的卖票程序,通过分析线程的生命周期、线程的分类以及线程安全问题,结合实际案例讲解了如何在实际项目中应用多线程技术。
Java后端工程师
2017/12/15
1.1K0
多线程基础
例子:单核CUP执行两件事,串行执行时间快,还是多线程执行快? 答:串行执行快。因为单核,执行的总时间一样,而多线程增加了线程切换的时间。
冬天vs不冷
2025/01/20
910
多线程基础
Java 多线程详解(三)------线程的同步
Java 多线程详解(一)------概念的引入:https://cloud.tencent.com/developer/article/1012542 Java 多线程详解(二)------如何创建
IT可乐
2018/01/04
9170
Java 多线程详解(三)------线程的同步
【19】JAVASE-多线程专题【从零开始学JAVA】
Java 是第一大编程语言和开发平台。它有助于企业降低成本、缩短开发周期、推动创新以及改善应用服务。如今全球有数百万开发人员运行着超过 51 亿个 Java 虚拟机,Java 仍是企业和开发人员的首选开发平台。
用户4919348
2024/05/25
1350
【19】JAVASE-多线程专题【从零开始学JAVA】
Java多线程之Runable与Thread
Java多线程是Java开发中的基础内容,但是涉及到高并发就有很深的研究可做了。 最近看了下《Java并发实战》,发先有些地方,虽然可以理解,但是自己在应用中很难下手。   所以还是先回顾一下基础知
用户1154259
2018/01/17
9290
Java多线程之Runable与Thread
Java多线程与并发
进程是资源分配的基本单位,所有与进程有关的资源都记录在进程控制块PCB中,以表示进程拥有这些资源或者正在使用它们,进程也是抢占处理机的调度单位,它拥有完整的虚拟内存地址空间,当进程发生调度时,不同的进程拥有不同的地址空间,而同一进程内的不同线程共享同一地址空间。与进程相对应,线程与资源分配无关,它属于某一个进程,并与进程内的其它线程共享进程的资源。
ha_lydms
2023/08/10
2090
Java多线程与并发
Java多线程与并发
文章目录 线程和进程的区别 JDK选择长期版本 线程和进程的区别 基础 由来 区别 关系 代码查看主线程 Thread中的start方法和run方法的区别 效果图 代码 分析源码 没有可比性run和star Thread和Runnable是什么关系 本质 源码 代码演示 普通Thread.start()线程-效果图 MyThread 通过Thread类启动Runnable多线程-效果图 MyRunnalbe MyRunnableMain 如何实现处理线程的返回值 如何给run()方法传参 实现的方式主要有三
瑞新
2022/05/11
5010
Java多线程与并发
Java 多线程学习
这些动作都可以抽象为任务,虽然看起来一心二用,但人只有一个大脑,在一个时间片刻只能处理一个任务。
默 语
2024/11/20
1030
Java 多线程学习
第二十四天 多线程-多线程&线程安全【悟空教程】
进程:进程指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能。
Java帮帮
2018/07/26
6540
第二十四天 多线程-多线程&线程安全【悟空教程】
多线程
线程类 Java使用 java.lang.Thread 类代表线程,所有的线程对象都必须是Thread类或其子类的实例 每个线程的作用是 完成一定的任务,实际上就是执行一段程序流即一段顺序执行的代码 Java使用线程执行体来代表这段程序流。
JokerDJ
2023/11/27
1950
多线程
相关推荐
【Java多线程】的学习总结
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验