多线程基础

一、什么是线程?

这里需要理解程序、进程、线程的概念和它们之前的关系

  • 程序:程序是完成特定任务、用某种语言编写的一组指令的合集。程序是静态的
  • 进程:是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有产生、存在和消亡的过程。作为系统进行资源分配单位,系统在运行时会给每个进程分配不同的内存区域。进程是程序的实体,一个程序可以有多个进程
  • 线程:进程可进一步细化为线程,是一个程序内部的一条执行路径。一个进程可以有多个线程;同一进程中的多条线程将共享该进程中的全部系统资源,但同一进程中的多个线程有各自的运行栈、程序计数器;进程中的多条线程可并行执行不同的任务,但多个线程操作共享资源可能会带来安全隐患

二、实现

线程实现分为4种方式,实现Runnable接口、实现Callable接口、继承Thread类、线程池,如下代码

// 请注意,此写法在并发的情况下是线程不安全的,后面的篇幅有修正
// 继承Tread
public class SellerJerry implements Runnable{
    private int ticket = 100;
    @Override
    public void run() {
        while(true){
            if(ticket > 0){
            	System.out.println("余票充足,可出票。票号:"+ticket);
                ticket--;
            }else{
                break;
            }
        }
    }
}

// 实现Runnable接口
public class SellerTom extends Thread{
    private int ticket = 100;
    @Override
    public void run() {
        while(true){
            if(ticket > 0){
            	System.out.println("余票充足,可出票。票号:"+ticket);
                ticket--;
            }else{
                break;
            }
        }
    }
}

//实现Callable接口
public class SellerJam implements Callable{
    @Override
    public Object call(){
        int sum = 0;
        return sum;
    }
}

// 测试
@SpringBootTest
public class ThreadTomTest {
    @Test
    public void test1() {
        Runnable sellerJerry = new SellerJerry();
        Thread jerry = new Thread(sellerJerry);//创建
        jerry.start();//启动线程		

        Thread tom = new SellerTom();//创建
        tom.start();//启动线程
    
        SellerJam  jam = new SellerJam();
    }
}

三、线程的生命周期

创建 NEW、就绪、运行 RUNNABLE、阻塞 BLOCKED/WATTING/TIMED_WATTING、死亡TERMINATED

简单实现如代码,在进入就绪状态之后,等待cpu分配资源,即可进入运行状态,执行完run()方法之后,线程结束。线程的最终状态一定是死亡。线程执行的过程和各个生命周期之间可能出现的转换,如下图

四、线程安全问题

当多个线程操作共享数据且操作为非原子性操作时,就会出现线程安全问题(如经典的售票、取款等问题)。解决的办法有如下几种

1.synchronized同步代码块,语法为:synchronized(同步监视器){ //需要被同步的代码 }

  • 操作共享数据的代码,即为需要被同步的代码
  • 共享数据:多个线程共同操作的变量。如ticket就是共享数据 
  • 同步监视器:俗称锁。任何一个类的对象,都可以充当锁。多个线程必须要共有一把锁。
// 还是用上面的代码,改为线程安全的写法
// 实现Runnable接口
public class SellerJerry implements Runnable {
    private int ticket = 100;
    @Override
    public void run() {
        while (true) {
            synchronized (this) {// 当前对象为锁
                if (ticket > 0) {
                    System.out.println("余票充足,可出票。票号:" + ticket);
                    ticket--;
                }else {
                    break;
                }
            }
        }
    }
}

// 继承Thread类
public class SellerTom extends Thread{
    private int ticket = 100;
    @Override
    public void run() {
        while (true) {
            synchronized (SellerTom.class) {// 当前对象为锁
                if (ticket > 0) {
                    System.out.println("余票充足,可出票。票号:" + ticket);
                    ticket--;
                }else {
                    break;
                }
            }
        }
    }
}

@SpringBootTest
public class ThreadTomTest {
	@Test
	public void test1() {
		Runnable sellerJerry = new SellerJerry();
		Thread jerry1 = new Thread(sellerJerry);//创建
		Thread jerry2 = new Thread(sellerJerry);//创建
		Thread jerry3 = new Thread(sellerJerry);//创建
		jerry1.start();//就绪
		jerry2.start();//就绪
		jerry3.start();//就绪
		
		Thread tom1 = new SellerTom();//创建
		Thread tom2 = new SellerTom();//创建
		Thread tom3 = new SellerTom();//创建
		tom1.start();//就绪
		tom2.start();//就绪
		tom3.start();//就绪
	}
}

2.同步方法

//实现Runnable
public class SellerJerry implements Runnable{
    private int ticket = 100;
    @Overried
    public void run(){
        while(true){
            sellerTicket();
        }
    }
    // 抽取方法
    private snychorized void sellerTicket(){
        if(ticket > 0){
            System.out.println("余票充足,可出票。票号:" + ticket);
            ticket--;
        }
    }
}
//继承Thread
public class SellerTom implements Runnable{
    private static int ticket = 100;
    @Overried
    public void run(){
        while(true){
            sellerTicket();
        }
    }
    // 抽取方法
    private static snychorized void sellerTicket(){
        if(ticket > 0){
            System.out.println("余票充足,可出票。票号:" + ticket);
            ticket--;
        }
    }
}

优点:同步的方式,可以解决线程安全的问题

缺点:操作同步代码时,只能有一个线程参与,其他线程等待,相当于一个单线程的过程,效率低

3.Lock锁方式

public class SellerJerry implements Runnable{
    private int ticket = 100;
    private ReentrantLock lock = new ReentrantLock(true);// 入参true表示公平
    @Overried
    public void run(){
        while(true){
            try{
                lock.lock();//获取锁
                
                if(ticket > 0){
                    System.out.println("余票充足,可出票。票号:" + ticket);
                    ticket--;
                }else{
                    break;
                }
            }finally(){
                lock.unlock();//释放锁
            }
        }
    }
}

五、线程之间的通信

  • wait():执行此方法,当前线程进入阻塞状态,并且释放锁
  • notify():执行此方法,会唤醒被wait的一个线程。多个线程被wait,唤醒优先级高的线程
  • notifyAll():执行此方法,会唤醒所有被wait的线程
  • 使用synchronized,这三个方法都只能在同步代码块里面使用,并且调用者为同步监视器
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值