java基础之多线程的基础基础知识

Java线程:概念、创建方式与对比
本文围绕Java线程展开,介绍了进程与线程的区别,阐述了线程的同步异步、并发并行、高并发、临界区、阻塞非阻塞等重要概念。详细讲解了实现Runnable接口、继承Thread类、使用Callable和Future创建线程的方式,并对比了各创建方式的优缺点,还提及常用方法。

文章目录

1 、进程与线程的区别

什么是进程

进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。如下图所示,在 windows 中通过查看任务管理器的方式,我们就可以清楚看到 window 当前运行的进程(.exe文件的运行)。

什么是多线程

线程与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。

2 、关于线程的几个重要的概念

同步和异步
同步和异步通常用来形容一次方法调用。同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为。异步方法调用更像一个消息传递,一旦开始,方法调用就会立即返回,调用者可以继续后续的操作。 关于异步目前比较经典以及常用的实现方式就是消息队列:在不使用消息队列服务器的时候,用户的请求数据直接写入数据库,在高并发的情况下数据库压力剧增,使得响应速度变慢。但是在使用消息队列之后,用户的请求数据发送给消息队列之后立即 返回,再由消息队列的消费者进程从消息队列中获取数据,异步写入数据库。由于消息队列服务器处理速度快于数据库(消息队列也比数据库有更好的伸缩性),因此响应速度得到大幅改善。
并发(Concurrency)和并行(Parallelism)
并发和并行是两个非常容易被混淆的概念。它们都可以表示两个或者多个任务一起执行,但是偏重点有些不同。并发偏重于多个任务交替执行,而多个任务之间有可能还是串行的。而并行是真正意义上的“同时执行”。

多线程在单核CPU的话是顺序执行,也就是交替运行(并发)。多核CPU的话,因为每个CPU有自己的运算器,所以在多个CPU中可以同时运行(并行)。

高并发
高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求。

高并发相关常用的一些指标有响应时间(Response Time),吞吐量(Throughput),每秒查询率QPS(Query Per Second),并发用户数等。

临界区
临界区用来表示一种公共资源或者说是共享数据,可以被多个线程使用。但是每一次,只能有一个线程使用它,一旦临界区资源被占用,其他线程要想使用这个资源,就必须等待。在并行程序中,临界区资源是保护的对象。
阻塞和非阻塞
非阻塞指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回,而阻塞与之相反。

以上内容转自别人博客,尊重原创

3 、线程创建的两种方式

3.1、实现 Runnable接口

public class ThreadOneTest implements Runnable{
	@Override
	public void run() {
		for (int i = 0; i < 5; i++) {
			System.out.println("哼哼的博客"+i);
		}
		
	}
	public static void main(String[] args) {
		ThreadOneTest threadOneTest = new ThreadOneTest();
		Thread thread = new Thread(threadOneTest);
		thread.start();
	}
	
}

3.2、继承Thread类

话不多说,直接代码演示

public class ThreadOneTest2 extends Thread{
	@Override
	public void run() {
		for (int i = 0; i < 5; i++) {
			System.out.println("哼哼的博客"+i);
		}
		
	}
	public static void main(String[] args) {
		ThreadOneTest2 thread = new ThreadOneTest2();
		thread.start();
	}
	
}

3.3、使用Callable和Future创建线程

创建Callable接口的实现类,重写call(),call() 方法就是线程的执行体,call() 有返回值。
使用FutureTask类的实例,来包装Callable对象,即把callable的实例以形参的方式传入new FutureTask()的构造函数中
使用FutureTask对象作为Thread对象的target创建启动线程
通过FutureTask实例对象调用get()方法得到子线程的返回值

public class Demo {
    private static void main(String[] args) {
        //创建Callable对象
        ThirdThreadCallable callable = new  ThirdThreadCallable();
        //创建FutureTask对象,并把callable以形参的方式传入FutureTask的构造方法内
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        for (int i = 0; i < 10; i++) {
            // 通过Thread类的currentThread方法可以得到当前的线程名
            System.out.println(Thread.currentThread().getName()+" "+i);
            if (i==5) {
                //创建线程并启动
                new Thread(futureTask, "有返回值的线程").start();
            }
        }
        //获取子线程的返回值
        try {
            System.out.println("子线程的返回值: "+futureTask.get());
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
/**
 1. ThirdThreadCallable  实现Callable 接口
 */
class ThirdThreadCallable implements Callable<Integer> {
    int i ;
    //call()方法称之为线程方法执行体,且该方法有返回值,可通过FutureTask实例对象调用get()方法得到子线程的返回值
    @Override
    public Integer call() throws Exception {
        for (i=0; i <10; i++) {
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
        return i;
    }
}

3.4、创建线程的三种方式对比

3.4.1、使用实现Runnable 、Callable接口的方式创建多线程

优点

  1. 线程类只是实现了Runnable接口或Callable接口,同时还可以继承其他类
  2. 多个线程可以共享一个target对象,非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码、数据分开,形成清晰的模型,较好的体现了面向对象的思想

缺点

  1. 编程稍微复杂,如果需要访问当前线程,必须使用Thread.currentThread()方法
3.4.2、使用继承Thread类的方式创建多线程

优点

  1. 编写简单,如果要访问当前线程,无需使用Thread.currentThread()方法,可以直接使用this的方式获取当前线程

缺点

1.因为线程类已经继承了Thread类,所以不能再继承其他的父类

一般情况下,项目中推荐使用Runnable接口或Callable接口创建多线程。

4、常用的方法


static Thread currentThread() 
返回对当前正在执行的线程对象的引用。 

String getName() 
返回此线程的名称。  

int getPriority() 
返回此线程的优先级 

Thread.State getState() 
返回此线程的状态。 

void interrupt() 
中断这个线程。  
static boolean interrupted() 
测试当前线程是否中断。  
boolean isAlive() 
测试这个线程是否活着。 

void join() 
等待这个线程死亡。  

void join(long millis) 
等待这个线程死亡最多 millis毫秒。  

void join(long millis, int nanos) 
等待最多 millis毫秒加上 nanos纳秒这个线程死亡。 

void run() 
如果这个线程使用单独的Runnable运行对象构造,则调用该Runnable对象的run方法; 否则,此方法不执行任何操作并返回。  

void setName(String name) 
将此线程的名称更改为等于参数 name 。  

void setPriority(int newPriority) //优先级的参数是1-10,数字越大,优先级又高
更改此线程的优先级。  

static void sleep(long millis) 
使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。  

static void sleep(long millis, int nanos) 
导致正在执行的线程以指定的毫秒数加上指定的纳秒数来暂停(临时停止执行),这取决于系统定时器和调度器的精度和准确性。  

static void yield() 
对调度程序的一个暗示,即当前线程愿意产生当前使用的处理器。  

多线程的生产者消费者模式,请点击此处

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值