【线程、线程池】

文章介绍了Java中线程的三种类型:主线程、用户线程和守护线程,以及如何通过继承Thread或实现Runnable接口创建线程。讨论了线程同步的重要性,包括线程礼让和插队的概念,以及Lock锁和notify(),notifyAll()在多线程协调中的作用。最后提到了线程池的概念。

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

线程有三类

主线程:系统线程,如Java中的Java虚拟机(主线程是最先执行的)
用户线程:Java中main函数执行的那些玩意
守护线程(精灵):比如Java中的GC(垃圾回收器,他是为主线程服务的,当系统启动后,GC就随之产生了;Java虚拟机断掉了,GC也就不干活了)

在这里插入图片描述

实现线程的过程(有两个方法):

实现线程的两个前提条件(继承Thread是最方便的,但是Java是单继承的,很可能会和其他的继承冲突;所以还可以通过实现Runnable接口,不过它要额外的通过写一个Thread类来执行start()方法)

方法一:继承一个父类Thread
方法二:实现一个接口Runnable

我们都必须重写run()方法,
因为这个方法来源于cpu,是操作系统给JDK提供的接口,JDK将它包装成run(),其实我们执行的线程就是写在run()方法中的代码执行起来了

new一个线程对象(我们是无法调用run()的,它只有cpu才可以调用,cpu分配时间碎片才能开始执行)
我们只能调用start()方法(这个方法继承自Thread),它会使这个线程除了cpu资源的其它资源都得到满足,让它进入就绪状态,在就绪队列中等待着cpu执行它

public class Thread01 {

    public static void main(String[] args) throws InterruptedException{
        Cat cat=new Cat(); //创建Cat对象,可以当作线程使用
        cat.start(); //启动子线程
        //说明:当main线程启动一个子线程 Thread-0,主线程不会阻塞,会继续执行,这时 主线程和子线程会交替执行
        System.out.println("主线程继续执行"+Thread.currentThread().getName());
        for (int i = 0; i <10 ; i++) {
            System.out.println("主线程 i="+i);
            Thread.sleep(1000);
        }
    }
}

/*
1 当一个类继承了Thread类,该类就可以当作线程类使用
2 我们会重写run方法,写上自己的业务代码
3 Thread类 内部也是实现了 Runnable 接口的run方法
*/
class Cat extends Thread{

    int times=0;
    @Override
    public void run() {
        while (true){
            //该线程每隔1秒种,在控制台输出 “喵喵,我是小喵喵”
            System.out.println("喵喵,我是小喵喵 "+(++times)+" 线程名="+Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(times==80){
                break; //当times到80,退出while,这时线程也就退出。。。
            }
        }
    }
}

当程序执行时,之后开启main方法这个主线程,则进行一个进程的执行,若有子线程,子线程A.start()开启一个子线程,当主线程和子线程全部执行完毕,进程结束。注意:只有所有的线程结束,进程才会结束

class Dog implements Runnable { //通过实现Runnable接口,实现线程
    int count = 0;
    @Override
    public void run() { //普通方法
        while (true) {
            System.out.println("小狗汪汪叫..hi" + (++count) + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);  //休眠1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (count == 10) {
                break;
            }
        }
    }
}

public class Thread02 {
    public static void main(String[] args) {
        Dog dog = new Dog();
        Thread thread = new Thread(dog); //创建了Thread对象,把 dog对象(实现Runnable),放入Thread
        thread.start(); //启动一个子线程
    }
}

在这里插入图片描述

继承Thread VS 实现Runnable的区别

1 从Java设计来看,通过继承Thread或者实现Runnable接口创建线程本质上没有区别,从jdk帮助文档上看Threrad类本身就是实现了Runnable接口。

2 实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制,建议使用Runnable。

线程常用的方法

1 setName //设置线程名称,使之与参数name相同
2 getName //返回线程的名称
3 start //是该线程开始执行;java虚拟机底层调用该线程的start0()方法
4 run //调用该线程的run方法
5 setPriority //更改线程的优先级
6 getPriority //获取线程的优先级
7 sleep //在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
8 interrupt //中断线程,不是停止,一般用于正在休眠的线程
9 线程优先级:MAX_PRIORITY MIN_PRIORITY NORM_PRIORITY

在这里插入图片描述

线程礼让

yield线程礼让,让出cpu,让其他线程执行,但是礼让的时间不确定,所以也不一定礼让成功

在这里插入图片描述

线程插队

class T2 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            try {
                Thread.sleep(1000); //休眠1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("子线程(大哥)吃了 "+i+" 个包子");
        }
    }
}

public static void main(String[] args) throws InterruptedException {
        T2 t2=new T2();
        t2.start();
        for(int i=0;i<20;i++){
            Thread.sleep(1000);
            System.out.println("主线程(小弟)吃了 "+i+" 个包子");
            if (i==5){
                System.out.println("主线程(小弟)让(大哥)先吃");
                // join 线程插队 一定会成功
                // t2.join();
                // 礼让 不一定会成功
                Thread.yield();
                System.out.println("子线程(老大)吃完了,我们接着吃");
            }
        }
}

线程同步

在这里插入图片描述

为什么要线程同步

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

Lock锁

public class Lock {
    public static void main(String[] args) {
        BuyTickett buyTicket = new BuyTickett();
        new Thread(buyTicket,"a").start();
        new Thread(buyTicket,"b").start();
        new Thread(buyTicket,"c").start();
    }
}

class BuyTickett implements Runnable{

    private int ticketNum = 10;
    boolean flag = true;

    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (flag){
            try {
                lock.lock();
                try {
                    buy();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }finally {
                lock.unlock();
            }
        }
    }

    private void buy() throws InterruptedException {
        if (ticketNum <= 0){
            flag = false;
            return;
        }
        Thread.sleep(100);
        System.out.println(Thread.currentThread().getName()+"获得了第"+ticketNum--+"张票");
    }
}

在这里插入图片描述

在这里插入图片描述

notify()

一个线程调用共享对象的notify)方法后,会唤醒一个在该共享变量上调用wait系列方法后被挂起的线程。一个共享变量上可能会有多个线程在等待,具体唤醒哪个等待的线程是随机的。

此外,被唤醒的线程不能马上从 wait方法返回并继续执行,它必须在获取了共享对象的监视器锁后才可以返回,也就是唤醒它的线程释放了共享变量上的监视器锁后,被唤醒的线程也不一定会获取到共享对象的监视器锁,这是因为该线程还需要和其他线程一起竞争该锁,只有该线程竞争到了共享变量的监视器锁后才可以继续执行。

类似wait系列方法,只有当前线程获取到了共享变量的监视器锁后,才可以调用共享变量的notify()方法,否则会抛出IllegalMonitorStateException异常。

ackage thread.notify;
 
public class NotifyTest {
	private static volatile Object resourceA = new Object () ;
	public static void main(String[] args) throws InterruptedException {
		Thread threadA = new Thread (new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
					//获取resourceA共享资源的监视器锁
					synchronized (resourceA) { 
						System.out.println("threadA get resourceA lock");
						try {
							System.out.println("threadA begin wait" ) ;
							resourceA.wait() ;
							System.out.println("threadA end wait" ) ;
					}catch (InterruptedException e) {
						e.printStackTrace() ;
					} 
				}
			}
		});
		Thread threadB = new Thread (new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
					//获取resourceA共享资源的监视器锁
					synchronized (resourceA) { 
						System.out.println("threadB get resourceA lock");
						try {
							System.out.println("threadB begin wait" ) ;
							resourceA.wait() ;
							System.out.println("threadB end wait" ) ;
					}catch (InterruptedException e) {
						e.printStackTrace() ;
					} 
				}
			}
		});
		Thread threadC = new Thread (new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
					//获取resourceA共享资源的监视器锁
					synchronized (resourceA) { 
						System.out.println("threadC begin notify");
						resourceA.notify();
						//resourceA.notifyAll();
				}
			}
		});
		threadA.start() ; 
		threadB.start() ; 
		Thread.sleep(1000);
		threadC.start () ;
		
	}
	
}

notifyAll()

不同于在共享变量上调用notify()函数会唤醒被阻塞到该共享变量上的一个线程,notifyAll()方法则会唤醒所有在该共享变量上由于调用wait系列方法而被挂起的线程。

线程池

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

线程池基本知识

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值