前言:

  互联网时代已经发展到了现在。从以前只考虑小流量到现在不得不去考虑高并发的问题。扯到了高并发的问题就要扯到线程的问题。你是否问过自己,你真正了解线程吗?还是你只知道一些其他博客里写的使用方法。下面让我们先从线程的一些基础开始讲解并发这一个知识体系。

一、线程是什么?

  首先我们要明白线程是什么,下面是我从百度百科摘过来的概念:

线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。

  这个概念比较晦涩难懂,我们可以这样理解线程表示的就是一条单独的执行流,一个程序可以包括很多子任务,而这些子任务就是一条线程。这个线程有自己的程序执行计数器,也有自己的栈。程序就是一个进程。平常我们所说的多线程不是很多个程序在一起运行,那叫多进程。多线程则是一个程序里面很多个任务在同时执行。(注意:通常多线程在OS底层是通过时间片分配来实现多线程的,实际上每一个时间片只运行了一条线程)

二、怎么创建线程?

1、继承Thread

public class StudyThread extends Thread {
@Override
public void run() {
System.out.println("学习多线程");
}
}

  你可以理解run方法就是类似于单线程中的main函数,如果这个线程执行,那么就会从run方法内的第一条语句开始执行到结束。但是线程现在就开始执行了吗?不是的,要让线程启动必须要先创建出StudyThread这个类的对象,而后开始调用start方法。注意:如果我们没有调用start方法,而是直接调用run方法,那你就可以认为只是调用了一个普通方法。程序并没有达到多线程,还是单线程。如果调用了start方法,程序就有了两条执行流,新的执行run方法,旧的就继续执行main函数。

  如果是在单CPU的机器上,同一时刻只能有一个线程执行,而多CPU的机器同一时刻就可以有多个线程同时执行。当所有线程执行完后程序才退出。

2、实现Runnable接口

  像我们之前说的设计模式,很多设计模式都涉及到继承,但是Java中只支持单线程。所以我们通常不会用上面的方式,而是去实现Runnable接口。

public class StudyRunnable implements Runnable {
@Override
public void run() {
System.out.println("hello");
}
}

  其余的和继承Thread一样。但是有一点不同的就是

public static void main(String[] args) {
Thread studyThread = new Thread(new StudyRunnable());
studyThread.start();
}

  我们需要创建一个线程类,并且传Runnable对象进去。说到底我们操作的还是Thread类,不过是将继承去掉罢了。

三、线程有哪些方法和属性?

  接下来我们直接看源码来解释

public class Thread implements Runnable {
//线程初始化入口
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
//初始化方法
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
} //真正的初始化方法
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
} this.name = name; Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
/* Determine if it's an applet or not */ /* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
} /* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
if (g == null) {
g = parent.getThreadGroup();
}
}
g.checkAccess(); /*
* Do we have the required permissions?
*/
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
} g.addUnstarted(); this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize; /* Set thread ID */
tid = nextThreadID();
}
}

  我们注意,在真正的初始化方法中,有以下这些属性会被初始化:

1、Tread类中重要的属性

1.1、name

  线程的名字默认是Thread-后跟一个编号,可以使用默认,通过getName方法得到,也可以通过setName方法自定义名字。

1.2、group

  所属线程组,一个线程必然有所属线程组。Thread与ThradGroup的关系就像元素与集合的关系。

1.3、daemon

  前面我们说过,启动线程就会启动一条单独的执行流,如果整个线程都结束,那么程序就会结束,但是当整个程序只剩下daemon的时候,程序也会退出。daemon线程一般是其他线程的辅助线程,主要作用是负责垃圾回收。

1.4、priority

  线程中的优先级,从1到10,优先级逐渐升高,默认为5。操作方法是用getPriority,setPriority

1.5、stackSize

  预期堆栈大小,不指定默认为0,0代表忽略这个属性。与平台相关,不建议使用该属性。

2、Tread类中一些重要的方法

2.1、getState()

  得到当前线程的线程状态。Thread.State是枚举类型,有NEW(没有调用start的线程状态)、RUNNABLE(调用start后线程在执行run方法且没有阻塞的状态)、BLOCKED(线程被阻塞)、WAITING(线程被阻塞)、TIMED_WAITING(线程被阻塞)、TERMINATED(线程运行结束后的状态) 5种。

2.2、isAlive()

  线程被启动后,run方法结束前,线程都是活的。

2.3、sleep(),yield(),join()

  上面三个方法都可以让控制线程的执行,sleep是让线程睡眠的方法,yield是告诉操作系统调用该方法的线程不急着执行,可以先让其他线程执行,当然操作系统可能接受yield的建议,也有可能不接受。join方法则是将其他线程中断,让调用该方法的线程先执行完再执行其他线程,如果这个执行的过程中被中断,就会抛出Interrupted-Exception

四、总结

  这篇博客只是先简单介绍了一下线程,让我们知道了线程的创建方法,线程初始化的过程,以及线程中一些常用属性和方法。但是这只是Java并发的皮毛。后续会介绍更深入的知识点。

Java并发——线程介绍的更多相关文章

  1. Java 并发 线程同步

    Java 并发 线程同步 @author ixenos 同步 1.异步线程本身包含了执行时需要的数据和方法,不需要外部提供的资源和方法,在执行时也不关心与其并发执行的其他线程的状态和行为 2.然而,大 ...

  2. Java 并发 线程的优先级

    Java 并发 线程的优先级 @author ixenos 低优先级线程的执行时刻 1.在任意时刻,当有多个线程处于可运行状态时,运行系统总是挑选一个优先级最高的线程执行,只有当线程停止.退出或者由于 ...

  3. Java 并发 线程属性

    Java 并发 线程属性 @author ixenos 线程优先级 1.每当线程调度器有机会选择新线程时,首先选择具有较高优先级的线程 2.默认情况下,一个线程继承它的父线程的优先级 当在一个运行的线 ...

  4. Java 并发 线程的生命周期

    Java 并发 线程的生命周期 @author ixenos 线程的生命周期 线程状态: a)     New 新建 b)     Runnable 可运行 c)     Running 运行 (调用 ...

  5. Java并发——线程安全、线程同步、线程通信

    线程安全 进程间"共享"对象 多个“写”线程同时访问对象. 例:Timer实例的num成员,即add()方法是用的次数.即Timer实例是资源对象. class TestSync ...

  6. Java并发--线程池的使用

    在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统 ...

  7. Java并发—线程池框架Executor总结(转载)

    为什么引入Executor线程池框架 new Thread()的缺点 每次new Thread()耗费性能 调用new Thread()创建的线程缺乏管理,被称为野线程,而且可以无限制创建,之间相互竞 ...

  8. JVM之java并发 ——线程安全与锁优化

    概述 人们很难想象现实中的对象在一项工作进行期间,会被不停地中断和切换,对象的属性(数据)可能会在中断期间被修改和变“脏”,而这些事情在计算机世界中则是很正常的事情.有时候,良好的设计原则不得不向现实 ...

  9. Java并发-线程池篇-附场景分析

    作者:汤圆 个人博客:javalover.cc 前言 前面我们在创建线程时,都是直接new Thread(): 这样短期来看是没有问题的,但是一旦业务量增长,线程数过多,就有可能导致内存异常OOM,C ...

随机推荐

  1. 一步一步理解 python web 框架,才不会从入门到放弃 -- 简单登录页面

    上一节,我们基本了解了 Django 的一些配置,这一节,我们将通过一个简单的登录页面,进一步学习 Django 的使用. 新建项目 首先,新建一个 Django 项目,记得别弄错了哦. settin ...

  2. Vue学习小结(二)

    接上一批,小结(二). 三.导航内容(含左侧导航及顶部面包屑导航) 其实导航条主要根据element-ui的教程进行编写,官网:http://element-ui.cn/#/zh-CN/compone ...

  3. MIP 技术交流分享(3月15日)

    3月15日下午,MIP 团队工程师与58赶集的 Web 前端工程师进行了一次面对面的技术交流. 在这次交流中,MIP 工程师主要分享了 MIP 技术原理,MIP 加速原理,以及 MIP 为开发者提供的 ...

  4. .NET Core使用Quartz执行调度任务进阶

    一.前言运用场景 Quartz.Net是一个强大.开源.轻量的作业调度框架,在平时的项目开发当中也会时不时的需要运用到定时调度方面的功能,例如每日凌晨需要统计前一天的数据,又或者每月初需要统计上月的数 ...

  5. 前端笔记之ES678&Webpack&Babel(中)对象|字符串|数组的扩展&函数新特性&类

    一.对象的扩展 1.1对象属性名表达式 ES6可以在JSON中使用[]包裹一个key的名字.此时这个key将用表达式作为属性名(被当做变量求值),这个key值必须是字符串. var a = 'name ...

  6. [翻译 EF Core in Action 1.10] 应该在项目中使用EF Core吗?

    Entity Framework Core in Action Entityframework Core in action是 Jon P smith 所著的关于Entityframework Cor ...

  7. 有人WIFI模块使用详解

    补充 模块在连接路由器时如果希望模块固定IP 不过发现固定IP之后好像连接路由器的等待时间增加了 用的这一款 看一下现在可能用到了引脚 这个模块也有三种模式AP,STA,AP+STA 先说一下模块在A ...

  8. 前端项目中常用es6知识总结 -- 箭头函数及this指向、尾调用优化

    项目开发中一些常用的es6知识,主要是为以后分享小程序开发.node+koa项目开发以及vueSSR(vue服务端渲染)做个前置铺垫. 项目开发常用es6介绍 1.块级作用域 let const 2. ...

  9. Java 10 var关键字详解和示例教程

    在本文中,我将通过示例介绍新的Java SE 10特性——“var”类型.你将学习如何在代码中正确使用它,以及在什么情况下不能使用它. 介绍 Java 10引入了一个闪亮的新功能:局部变量类型推断.对 ...

  10. ASP.NET Core中使用GraphQL - 第三章 依赖注入

    ASP.NET Core中使用GraphQL ASP.NET Core中使用GraphQL - 第一章 Hello World ASP.NET Core中使用GraphQL - 第二章 中间件 SOL ...