Java | 多线程(创建、常见方法)


在操作系统中,线程是由系统内核提供的一些api在应用程序去调用的,而Java对其进行了进一步的封装(可移植性),封装成了Thread类,放在了java.lang的包内(java.lang内的类可以直接使用,无需引用)。

创建线程的方法(重点)

1.继承thread,重写run

class MyThread extends Thread{
	@Override
	public void run(){
		// 该方法是线程的入口方法
		
	}
}
// 调用
public static void main(String[] args){
	Thread t = new MyThread();
	t.start();
}

面试题->start()和run()异同->是否在系统中创造了新线程

  • start 和 run 都是 Thread 的成员。
  • run 只是描述了线程的入口(线程要做什么任务)。
  • 如果只执行 run,并没有创建新线程。
  • start 则是真正调用了系统API,在系统中创建了线程,让线程再调用run。

2.(继承thread)重写run,使用匿名内部类

// 调用
public static void main(String[] args){
	Thread t = new Thread(){
		@Override
		public void run(){
		// 该方法是线程的入口方法
		
		}
	};
	t.start();
}

3.实现Runnable,重写run

class MyRunnable implements Runnable{
	@Override
	public void run(){
		// 该方法是线程的入口方法
		
	}
}
// 调用
public static void main(String[] args){
	Runnable runnable = new MyRunnable();
	Thread t = new Thread(runnable);
	t.start();
}
  • Runnable表示的是一个“可以运行的任务”,这个任务是交给线程负责执行。还是交给其他的任务来执行,Runnable并不关心
  • 使用Runnable的写法,和直接继承Thread之间的区别,就是解耦合->把任务提取了出来,这样可以随时改成让其他代码执行该任务

4.(实现Runnable)重写run,使用匿名内部类

// 调用
public static void main(String[] args){
	Runnable runnable = new Runnable(){
		@Override
		public void run(){
			// 该方法是线程的入口方法
			
		}
	};
	Thread t = new Thread(runnable);
	t.start();
}

甚至还可以写为下面的样子

// 调用
public static void main(String[] args){
	Thread t = new Thread(new Runnable(){
		@Override
		public void run(){
			// 该方法是线程的入口方法
			
		}
	});
	t.start();
}

5.lambda表达式

Thread t = new Thread(()->{
	//此处直接写具体实现
});
t.start();

其他事项

命名

线程可以指定名字name,name不影响线程的执行,可以使用下面了两种方式来指定名字:

  • Thread(String name) :创建线程对象,并命名
  • Thread(Runnable target, String name):使用 Runnable 对象创建线程对象,并命名

线程常见属性

属性获取方法
IDgetId()
名称getName()
状态getState()
优先级getPriority()
是否后台线程*isDaemon()
是否存活isAlive()
是否被中断isInterrupted()

线程中断(终止、打断)

注:此时的中断与操作系统中的概念"中断"不同
Java中指的是让一个线程停止运行(销毁),在Java中,要销毁/终止线程,做法是比较唯一的,是想办法让run方法尽快执行结束(而不是像C++那样线程运行了一半直接进行中断),而且中断也不会强制停止,线程可以自行决定如何响应中断请求。
在操作系统中,中断通常是硬件中断或软件中断,是由硬件设备、定时器或软件异常等触发的异步信号。且常用于I/O操作(例如键盘输入、硬盘读写)、进程间通信等地方。

通过共享的标记来通知

注意,要给标记加上volatile来保证操作的原子性。
这样具有局限性,一些功能无法实现,最好是使用下一个方式。

class MyThread extends Thread{
	public volatile boolean isQuit = false;
	@Override
	public void run(){
		// 该方法是线程的入口方法
		while(!isQuit){
			
		}
	}
}
// 调用
public static void main(String[] args){
	Thread t = new MyThread();
	t.start();
	t.isQuit = true;
}

interrupt()方法来通知

通过 t.interrupt() 来中断:此方法就是将Thread对象内部(是否被中断)的标志位设置为true,当线程中检测到后,线程停止。
为什么示例代码不直接使用t.isInterrupted()而是使用Thread提供的类方法currentThread()?
因为t还没进行初始化,直接使用会报错,报错信息为:Variable ‘t’ might not have been initialized

public static void main(String[] args){
	Thread t = new Thread(new Runnable(){
		@Override
		public void run(){
			// 该方法是线程的入口方法
			while(!Thread.currentThread().isInterrupted()){
				try{
					Thread.sleep(5000);
				}catch(InterruptException e){
					e.printStackTrace();
					break;//直接结束线程,若不添加,则忽视中断请求
				}
			}
		}
	});
	t.start();
	t.intereupted();
}

interrupt() 和 Thread.sleep()的关系

  1. Thread.sleep()方法会让线程处于阻塞态,一般情况下,sleep休眠时间结束,才能唤醒。
  2. 但是当interrupt()被调用时,JVM会立刻将线程从阻塞状态唤醒并抛出InterruptedException异常,此时,线程就有机会处理中断请求。
  3. interrupt()不会强制终止线程,而是通过设置中断标志来改变通知线程改变行为或停止。
  4. sleep方法抛出异常后,同时会清除刚才设置的标志位。此时仿佛“设置标志位没有生效”。
  5. 但是Java这么设置是为了给程序员提供更多的选择:
    1. 直接无视该中断操作,继续执行。
    2. 在catch代码块中添加break,让线程直接结束。
    3. 添加其他代码,做一些其他的工作,完成后在结束。

线程等待

引入

线程之间的并发执行是无序的(随机的),这会给实际代码的允许带来一定的“麻烦”。比如在部分情况下,线程的结束必须按照规定的顺序来执行,在此问题下,Java引入了线程等待,其就是为了让线程的结束按照规定的顺序来执行。

实现

在Thread中,通过join()实现线程等待的效果,比如说由线程A调用B.join(),此时是A等待B先结束,随后A才能结束(注意顺序!)。一但调用B.join(),线程A就会阻塞,B执行完毕后,A才会结束阻塞,下面将进一步通过代码来说明:

Thread t = new Thread(()->{
	//此处直接写具体实现
});
// (在主线程中:)
t.start();
// 其他代码...
t.join();

线程状态

  • NEW:Thread对象已经有了,但start方法还未被调用。
  • TERMINATED:Thread对象还在,内核中的线程已经没了。
  • RUNNABLE:就绪状态——线程已经在CPU上执行了/正在排队等待上CPU执行。
  • TIMED_WAITING:阻塞,由于固定时间(sleep)产生的阻塞。
  • WAITING:阻塞,由于不固定时间(wait)产生的阻塞。
  • BLOCKED:阻塞,由于锁竞争(synchronized)导致的阻塞。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值