前往小程序,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 删除。

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Go语言学习系列——条件语句:if-else、switch——【坚果派-红目香薰】
if-else: 1. 条件表达式不需要括号 2. 大括号必须有,且必须在同一行 3. 支持初始化语句 4. 条件必须是布尔值 switch: 1. 不需要break语句 2. 支持多条件匹配 3. 支持无条件表达式的形式 4. fallthrough关键字用于继续执行下一个case 5. case后可以是表达式 6. default可选,位置灵活
红目香薰
2025/03/13
960
Go语言学习系列——条件语句:if-else、switch——【坚果派-红目香薰】
Go 语句
if...else 语句 | if 语句 后可以使用可选的 else 语句, else 语句中的表达式在布尔表达式为 false 时执行。 |
acc8226
2022/05/17
3900
go 流程控制之switch 语句介绍
我们先通过一个例子来直观地感受一下 switch 语句的优点。在一些执行分支较多的场景下,使用 switch 分支控制语句可以让代码更简洁,可读性更好。
贾维斯Echo
2023/10/18
3410
go语言慢速入门——流程控制语句
go的流程控制语句很有特色。if-else,for,switch-case。注意go没有while和do-while语句。除此之外go还有和特定类型绑定的流程控制模块。例如,用于容器类型的for-range循环。 go支持break,continue以及goto语句,另外go还支持特有的fallthrough语句。
zy010101
2022/07/28
4310
11.Go语言-流程控制
所谓流程控制就是指“程序怎么执行”或者说“程序执行的顺序”。程序整体上确实是从上往下执行,但又不单纯是从上往下。
面向加薪学习
2022/09/04
5540
第18节 switch语句
switch是一个条件语句,它计算表达式并将其与可能匹配的列表进行比较,并根据匹配执行代码块。它可以被认为是一种惯用的方式来写多个if else子句。
小尘哥
2019/07/19
6220
Golang语言社区--Go语言基础第五节流程控制
大家好,我是Golang语言社区(www.Golang.LTD)主编彬哥,今天给大家带来的是关于go语言中的流程控制。
李海彬
2018/03/08
7920
Golang语言社区--Go语言基础第五节流程控制
Go语言中的控制结构:条件语句和循环
在编程中,控制结构是指引导程序如何执行的一系列指令。Go语言提供了多种控制结构来处理条件判断和循环操作。本文将详细介绍Go语言中的条件语句和循环结构,通过实例和代码解释,帮助读者全面理解和应用这些基本构造。
数字扫地僧
2024/06/12
1520
【愚公系列】2022年7月 Go教学课程 018-分支结构之switch
Switch在一些计算机语言中是保留字,其作用大多情况下是进行判断选择。以Go语言来说,switch(开关语句)常和case default一起使用。
愚公搬代码
2022/08/01
4640
【愚公系列】2022年7月 Go教学课程 018-分支结构之switch
Go 语言基础入门教程 —— 流程控制篇:分支语句
分支语句会根据传入条件的不同,选择不同的分支代码执行。Go 语言的分支语句和 PHP 类似,只是不需要在每个分支结构中显式通过 break 语句退出:
学院君
2019/08/08
5500
100天精通Golang(基础入门篇)——第8天:Go语言程序的流程结构和条件语句
本文是《100天精通Golang(基础入门篇)》系列的第8天,介绍了Golang中程序的流程结构和条件语句。通过学习程序的流程结构,包括顺序结构、分支结构和循环结构,以及条件语句如if语句和switch语句,读者可以进一步掌握Golang的基础知识和语法。
猫头虎
2024/04/08
1590
100天精通Golang(基础入门篇)——第8天:Go语言程序的流程结构和条件语句
Go语言入门:分支结构
注意:其他很多编程语言是要求if后的条件判断表达式要用()括号括起来,但Go语言不需要!
阿珍
2025/02/06
670
Go语言入门:分支结构
Go语言条件语句
Go语言的条件语句在概念和流程上与其他编程语言完全相同,因此这里不再赘述,写法上结合了Python和C++,即条件表达式不需要加括号,但仍然保留了花括号。具体例子见下:
Steve Wang
2020/12/22
5870
三分钟学 Go 语言——条件语句+switch和type switch
switch 好理解,是一个替代if else else else接口而提出的,如下,switch 后跟变量,case 后跟常量,只要变量值和常量匹配,就执行该分支下的语句。
机智的程序员小熊
2020/04/21
5720
三分钟学 Go 语言——条件语句+switch和type switch
GO语言基础之条件语句if
如果condition为true时,执行上面{ 代码 } 之间的代码,如果为false时,执行else里面的代码
墨紫羽墨
2022/01/12
6960
Go通关03:控制结构,if、for、switch逻辑语句
您诸位好啊,我是无尘,今天学习下go语言的控制结构: if 条件语句 func main() { i:=6 if i >10 { fmt.Println("i>10") } else if i>5 && i<=10 { fmt.Println("5<i<=10") } else { fmt.Println("i<=5") } } 注意: if 后的表达无 ‘( )’ 每个条件分支中的 ‘{ }’ 是必须的。哪怕只
微客鸟窝
2021/08/18
3720
【Go语言刷题篇】Go从0到入门5:Map综合复习、条件语句、循环语句练习
问题描述:给定两个字符串des 和src ,判断 des能不能由 src 里面的字符构成,//如果可以,返回 true ;否则返回 false,src中的每个字符只能在 des 中使用一次。
程序员洲洲
2024/06/07
1340
【Go语言刷题篇】Go从0到入门5:Map综合复习、条件语句、循环语句练习
Go 语言学习之流程控制
在 Go 语言中,if...else... 语句的条件表达式必须是布尔类型,可以省略小括号,并且左大括号不能另起一行。通过代码,我们演示 if...else... 的标准使用方法。
frank.
2020/07/28
2690
Go 编程 | 连载 09 - 条件和循环表达式
需要注意的是 Go 中 if 控制语句的 { 不可以换行,必须要跟 if 关键字在同一行,否则会报错。
RiemannHypothesis
2022/09/26
2350
Go 编程 | 连载 09 - 条件和循环表达式
go语言基础3-控制语句
if 实例 func method(v int) int { if v >100 { return 100 }else if v < 0 { return 0 }else { return v } } //读取文件 func readFile(){ const filename = "branch/abc.txt" if constens, err := ioutil.ReadFile(filename); err
吐吐吐吐吐葡萄皮
2019/04/04
3660
go语言基础3-控制语句
推荐阅读
相关推荐
Go语言学习系列——条件语句:if-else、switch——【坚果派-红目香薰】
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验