当前位置: 移动技术网 > IT编程>开发语言>Java > Java并发——线程介绍

Java并发——线程介绍

2019年04月11日  | 移动技术网IT编程  | 我要评论

前言:

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

一、线程是什么?

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

线程(英语: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并发的皮毛。后续会介绍更深入的知识点。

 

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网