两种创建线程的方式

继承java.lang.Thread类

public class FirstThread extends Thread{
	
	private int i ;

	@Override 
	public void run() { // 真正去运行线程,执行任务
		for (i = 0; i < 50; i++) { 
			System.out.println(getName() + " 运行 : " + i);
		} 
	}
	
	public static void main(String[] args) { 
		//创建第一个线程 
		new FirstThread().start();  // 就绪
		//创建第二个线程 
		new FirstThread().start();  // 就绪,何时开始
	}
}
  • 上述程序有3个线程,main方法作为程序运行默认的主线程,还有main方法体中的创建的2个线程Thread-0 和 Thread-1。
  • Thread-0 和 Thread-1是乱序执行的,且不共享实例变量i。也即是 当使用继承Thread类来创建多
    线程时,多个线程之间无法共享线程类的实例对象。
  • Thread类的run()方法不执行任何操作并返回,因此继承Thread类一定要重写run(),否则没有线程
    执行体。

实现java.lang.Runnable接口

public class SecondThread implements Runnable{
	
	private int i ;

	@Override 
	public void run() { 
		for (i = 0; i < 50; i++) { 
			System.out.println(Thread.currentThread() + " 运行 : " + i); 
		} 
	}
	
	public static void main(String[] args) { 
		SecondThread st = new SecondThread(); 
		//通过new Thread(target, name)的方式创建线程 
		//以Runnable接口的实现类作为Thread的target来创建Thread线程对象 
		new Thread(st, "Thread-0").start(); 
		new Thread(st, "Thread-1").start(); 
	} 
}
  • 必须重写Runnable接口中的run()方法,以此来作为线程执行体。
  • 实际的线程对象仍然是Thread实例(即new 出来的Thread对象,只是该Thread对象负责执行其
    target中的run方法。
  • Runnable接口下创建的多个线程可以共享线程类的实例变量(因为打印结果中i是连续的),主要
    是因为多个线程共享一个target即上述代码中的st变量对应的SecondThread实例。
  • 输出结果中,Thread[Thread-0,5,main] ,5为线程的默认优先级。线程优先级值范围1-10,其中1
    为优先级最高。

推荐使用Runnable接口的方式

  • 多个线程可以共享一个 target ,适合多个相同线程来处理同一份资源
  • 线程类还可以继承其他类

线程的生命周期

在这里插入图片描述

线程有五种状态

  • 新建状态(New):新创建了一个线程对象。
  • 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程
    位于可运行线程池中,变得可运行,等待获取CPU的使用权。注意: 启动线程用start()方法,不要直接
    调用run()方法,否则JVM会把run()方法当作普通方法来执行,而不是多线程的线程执行体。
  • 运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
  • 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程
    进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
    – 等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
    – 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
    – 其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
  • 死亡状态(Dead):run()或call()方法执行结束或者因异常退出了run()方法,该线程结束生命周期。可以调用线程对象的isAlive()方法查看线程状态,线程处于new和dead状态时,该方法返回false。另外,不要对一个已经死亡的线程调用start()让它重新启动。

join线程

Thread 提供的join方法让一个线程等待另一个线程完成。当A线程调用B线程的join()方法,A线程将被阻塞,知道B线程执行完成后,再继续执行A线程。

public class JoinThread extends Thread{
    public void run(){
        // do something
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread() + " " + i);
        }
    }
    public static void main(String[] args) throws InterruptedException {
        JoinThread joinThread = new JoinThread();
        for (int i = 0; i < 100; i++){
            System.out.println("我是主线程:" + i);
            if (i == 20){
                var jt = new Thread(joinThread);
                jt.start();
                jt.join();
            }
        }
    }
}

就像插队一样
运行结果:

我是主线程:0
...
...
我是主线程:19
我是主线程:20
Thread[Thread-1,5,main] 0
Thread[Thread-1,5,main] 1
Thread[Thread-1,5,main] 2
Thread[Thread-1,5,main] 3
Thread[Thread-1,5,main] 4
我是主线程:21
我是主线程:22
...
...
我是主线程:99

sleep线程

让当前执行的线程暂停一段时间,进入阻塞状态,而非就绪状态。

public class SleepThread {
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            System.out.println("系统当前时间" + new Date());
            Thread.sleep(1000);
        }
    }
}

打印结果:

系统当前时间Fri May 28 20:04:34 CST 2021
系统当前时间Fri May 28 20:04:35 CST 2021
系统当前时间Fri May 28 20:04:36 CST 2021
系统当前时间Fri May 28 20:04:37 CST 2021
系统当前时间Fri May 28 20:04:38 CST 2021
系统当前时间Fri May 28 20:04:39 CST 2021
系统当前时间Fri May 28 20:04:40 CST 2021
系统当前时间Fri May 28 20:04:41 CST 2021
系统当前时间Fri May 28 20:04:42 CST 2021
系统当前时间Fri May 28 20:04:43 CST 2021

yield 线程

让当前执行的线程暂停,进入就绪状态,而非阻塞状态。(和sleep()线程不同)

调用A线程的yield()方法后,A让出CPU的使用权,进入就绪状态和其他线程一起参与下一次的资源调度竞争。处于就绪状态的多个线程中,谁的优先级最高会先获得执行的机会。

和sleep()方法不同的,yield()只会给同级优先级或更高优先级的线程执行机会,而sleep()暂停当前线程后会给其他线程执行,不区分优先级。

后台线程

后台线程(Daemon Thread)又称为守护线程,在后台运行并为其他线程提供服务,比如JVM的GC线程。

当所有前台线程都死亡之后,后台线程会自动死亡。

后台线程设置:

对象.setDaemon(true);

sleep和wait的异同

相同点:

  • sleep()通过传入“睡眠时间”作为方法的参数,时间一到就从“睡眠”中“醒来”,继续执行程序;wait()称为线程等待,传入等待时间,等待时间结束,就会继续执行。

不同点:

  • wait不传入等待时间便会进入无期限的等待,只能用notify()方法唤醒;
  • sleep()释放CPU执行权,但不释放同步锁;wait()释放CPU执行权也释放同步锁,使得其他线程可以使用同步控制块或者方法。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值