跳到主要内容

01、Java JUC 源码分析 - 并发编程线程基础

1.1什么是线程?

在讲什么是线程之前,我要提一个概念,那就是进程。那么什么是进程呢?进程是代码在数据集合上的一次运行活动,,是系统进行资源分配和调度的基本单位。线程其实就是进程的一个执行路径。一个进程中至少含有一个线程,进程中的多个线程共享资源。
众所周知,操作系统在分配资源的时候,是按照进程来分配的。但是其中有一种特殊情况,就是CPU资源,CPU资源是分配到线程的,也就是说,线程是CPU资源分配的基本单位。

1.2线程的创建与运行

在java中有3中创建线程的方式。分别如下:

  • 实现Runnable接口
  • 继承Thread类
  • 使用FutureTask方式

1.2.1继承Thread类创建并运行线程

public class ThreadTest {
   
     
	//继承Thread类,并重写run()方法
	public static class MyThread extends Thread{
   
     
		@Override
		public void run() {
   
     
			System.out.println("This is a Thread!");
		}
	}
	
	public static void main(String[] args) {
   
     
		//创建线程
		MyThread thread = new MyThread();
		//启动线程
		thread.start();
	}
}

这里我们要注意一点,启动线程我们用到了start方法。调用start方法后线程并没有进入运行状态而是进入了一个就绪的状态,当这个线程获取CPU时间片后线程才会进入运行状态,才会执行run方法里面的内容。
使用继承的好处就是,在run方法里面获取当前正在运行的线程只要用this关键字就可以了,没必要再使用Thread.currentThread().有优点就有缺点,缺点就是java没有多继承,也就是说如果继承了Thread类,那就不能再继承其他类了。另外任务和代码没有分离,当多个线程执行一样的任务的时候,这个时候就会用到多分任务代码。这个时候我们就可以用到Runnable接口来实现。

1.2.2实现Runnable接口创建并运行线程

public class RunnableTest {
   
     
	//实现runnable接口,并重写run方法
	public static class RunnableTask implements Runnable{
   
     
		
		@Override
		public void run() {
   
     
			System.out.println("This is a Thread!");
		}
	}
	public static void main(String[] args) {
   
     
		RunnableTask task = new RunnableTask();
		new Thread(task).start();
		new Thread(task).start();
	}
}

如上代码,两个线程就共享了一个任务的代码逻辑。另外如果有需要的话,上面的RunnableTask类还可以继承其他类,并且还可以实现其他接口。但是以上两种都有一个缺点,就是他们没有任务返回值。

1.2.3使用FutureTask创建并运行线程

public class FutureTaskTest {
   
     
	//创建一个任务类,并实现Callable接口,尖括号里为返回类型
	public static class CallableTask implements Callable<String>{
   
     

		@Override
		public String call() throws Exception {
   
     
			// TODO Auto-generated method stub
			return "This is a Thread";
		}
		
	}
	
	public static void main(String[] args) {
   
     
		//创建异步任务
		FutureTask<String> task = new FutureTask<>(new CallableTask());
		new Thread(task).start();
		
		try {
   
     
			String result = task.get();
			System.out.println(result);
		} catch (InterruptedException e) {
   
     
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ExecutionException e) {
   
     
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

如上代码CallableTask实现了Callable接口的call方法,然后在main函数里面,首先通过CallableTask创建异步任务,也就是一个FutureTask对象,程序中给这个对象命名为task。然后task作为任务使用线程启动它。等线程执行完后,返回值可以通过task.get()获得。

1.2.4小结

使用继承的好处就是方便传参数,你可以在子类里面设置成员变量,也可以通过构造方法将参数传递进来,而如果使用Runnable接口,那就只能使用主线程里定义的final变量。不好的地方就是java只支持单继承。然后前两个方式获得不了返回参数,但是FutureTask可以。

1.3线程的状态转换

这里我提供一张图片,图片中提及的各个方法,我会单独拿出来讲,放在后面的博文中。