zoukankan      html  css  js  c++  java
  • Java并发——线程介绍

    前言:

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

    一、线程是什么?

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

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

  • 相关阅读:
    [转载]C# TCP实现多个客户端与服务端 数据 与 文件的传输
    【转载】心跳机制
    paip.web数据绑定 下拉框的api设计 选择框 uapi python .net java swing jsf总结
    paip.复制文件 文件操作 api的设计uapi java python php 最佳实践
    paip.php 与js 的相似性以及为什么它们这么烂还很流行。。
    paip.重装系统后firefox火狐收藏夹的恢复
    paip.截取字符串byLastDot方法总结uapi python java php c# 总结
    paip.gui控件form窗体的原理实现以及easyui的新建以及编辑实现
    paip.python php的未来预测以及它们的比较优缺点
    paip.快捷方式分组管理最佳实践ObjectDock
  • 原文地址:https://www.cnblogs.com/Cubemen/p/10685525.html
Copyright © 2011-2022 走看看