欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JAVA多线程技术学习笔记(一)——多线程基本概念

程序员文章站 2022-05-04 18:18:55
...

JAVA多线程技术学习笔记(一)——多线程基本概念 

什么是进程?

我在一家公司面试的时候面试官问了我这样一个问题:什么是进程?我知道怎么创建线程,知道怎么操作,唯独就忘记了这个怎么表述,尴尬。我告诉他进程是这个

                                                         JAVA多线程技术学习笔记(一)——多线程基本概念

然后他就问我对,那这个是什么呢???我是没答上来,回去百度了答案是这么说的:“进程是操作系统结构的基础,是一次程序的执行;是一个程序及其数据在处理机上顺序执行时所发生的活动;是程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的独立单位。”

什么是线程?

线程是进程的一部分,一个进程可以分出好几个线程,比如:我使用网易云音乐,我可以下载歌曲,同时还能听歌,看评论等不同的功能同时在运行,这些不同的功能就可以理解为是一个“线程”。没有线程的进程可以被看做是单线程的,也是CPU调度的基本单位。

线程与进程的区别

线程的改变只是代表了CPU执行过程的改变,而没有发生进程拥有的资源变化,进程拥有一个完整的地址空间,不依赖线程的独立存在,而进程则相反,没有自己的独立空间,域进程内部的其他线程一起共享分配的所以资源。

如何使用JAVA创建一个线程?

既然现在已经大致知道了什么是Java多线程,那么如何创建一个java线程呢?创建Java线程的方式有一下两种

1.继承Thread

public class ThreadDome extends Thread {
	public void run() {
		System.out.println("继承Thread线程开启");
	}
	public static void main(String[] args){ 
		ThreadDome t1= new ThreadDome();
		t1.start();
	}
}

Thread类是一个Rannable接口的实现,采用这种方法创建一个线程最大的就行就是Java只支持单继承,继承了该类就不能进行其他的扩展。优点就是可以较为简单快捷的创建一个线程,获取这个线程可以直接使用this即可

2.实现Runnable接口

public class ThreadDome implements Runnable {
	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("实现Runnable线程开启");
	}
	public static void main(String[] args){ 
		Thread t1= new Thread(new ThreadDome());
		t1.start();
	}
}

Java支持多实现,所以实现Runnable的方法比较灵活,所以通常推荐采用这种创建线程模式.获取当前线程需采用Thread.currentThread()来获取

3.通过CallableFutureTask创建线程

public class ThreadDome implements Callable<String> {

	@Override
	public String call() throws Exception {
		// TODO Auto-generated method stub
		System.out.println("使用Callable创建线程开启");
		return null;
	}
	
	public static void main(String[] args){ 
		FutureTask<String> newFT=new FutureTask<>(new ThreadDome());
		Thread t1=new Thread(newFT);
		t1.start();
	}
	
}

这第三种方式个人感觉与第二种类似,感觉就像是创建了一个线程任务,当需要开启线程时就通过创建一个Thread对象将任务传入线程开启线程的执行。

Thread类中的常用的函数

线程的开启

run()star()
这两种方法都是可以执行线程run内的代码的,但是不同的是run方法是在本线程内执行,也就是单线程执行,可视为调用一个普通方法。而star是一个多线程的调用方式,所以开启多线程时需要使用的时star()方法。

线程的关闭

1.异常停止法(推荐使用)

interrupt:该方法是对线程打了一个停止标志,但是不能真正停止。

interrupted:测试线程是否中断,执行后将将状态标志清除为false。(还有 isInterrupt:测试线程是否是中断的。)

结合这两个函数停止线程的方式

public class Test1 extends Thread  {
	public static void main(String[] args) throws InterruptedException{
		Thread a=new Test1();
		a.start();
		Thread.sleep(200);
                 //打上中断标记
		a.interrupt();
	}
	@Override
	public void run() {
		super.run();
		int i=0;
		try{
		while(true){
			System.out.println(i++);
			if(Thread.interrupted()){
				//抛出中断错误
				throw new InterruptedException();
			}
		}
		}catch(InterruptedException e){
			System.out.println("thread is stop");
			e.printStackTrace();
		}
	}
}

2.直接停止法

stop():直接停止线程(已被弃用),暴力停止会导致一些清理性工作不能完成,还肯破坏数据同步处理

public class Test1 extends Thread  {
	public static void main(String[] args) throws InterruptedException{
		Thread a=new Test1();
		a.start();
	}
	@Override
	public void run() {
		super.run();
		int i=0;
		while(true){
				System.out.println(i++);
				if(i>100){
					this.stop();
				}
		}
	}
}

3.运行结束自动停止

线程暂停和启动

suspend()和resume():线程的暂停和重启,现在已经被弃用了。首先这两个方法有明显的缺点:对资源的独占,极易发送死锁。如下面代码例如下面这一段代码:
public class SuspendTest {
	
	public static void main(String[] args){
		Thread t1=new Thread(){
			public void run(){
				synchronizedprintln.synprint("a");
			}
		};
		t1.start();
		Thread t2=new Thread(){
			public void run(){
				System.out.println("抱歉资源被占用!");
				synchronizedprintln.synprint("b");
			}
		};
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		t2.start();
		
	}
}
class synchronizedprintln {
	synchronized  public static  void synprint(String a) {
		System.out.println("你好!线程开始");
		if("a".equals(a)){
			System.out.println("抱歉!线程停止");
			Thread.currentThread().suspend();
		}
		System.out.println("谢谢!线程停止");
	}
	
}

输出结果如下:



JAVA多线程技术学习笔记(一)——多线程基本概念
不仅如此,这两个方法还是非同步的(在之后的同步文章中会说明),所以被弃用了。
线程的优先级
currentThread():代指运行时的线程,可以通过这个方法来获取线程的一些信息。
sleep():使线程进入休眠状态(具体的在之后线程通信时讲)。
isAlive():判断线程是否处于休眠/活跃状态。
getId():获取线程的唯一标识。