线程学习笔记(一)

一、引入多线程

多线程

请看上图中的代码执行流程,就可以大概的理解多线程。说到多线程就一定会联系到单线程,所谓单线程就是程序只有一条执行路径。相反,多线程就是程序有多条执行路径。

二、进程的概述

1.要想了解多线程,必须先了解线程。了解线程就必须先了解进程。因为线程依赖于进程。

2.什么是进程:就是正在运行中的程序。进程是系统进行资源分配和调度的独立单位,每一个进程都有自己独立的内存空间和系统资源。

3.多进程的意义:在同一时间段内可以执行多个任务,极大的提高了CPU的使用率。单核的CPU在某一时刻只能执行一个任务。

三、线程的概述

1.什么是线程:在同一个进程中又可以执行多个任务,而这每一个任务可以看成一个线程。线程是程序执行路径和执行单元,是程序使用CPU的基本单元。

2.多线程的意义:不是提高程序的执行速度的,而是提高应用程序的使用率的。程序的执行其实都在抢CPU资源和CPU的执行权。多个进程都在争夺CPU这一资源,而其中一进程有多个执行路径,抢到这一资源的几率就更高。但线程的执行是由随机性的。

四、并行和并发

并行:逻辑上同时发生,是指在同一时间内同时运行多个程序。

并发:物理上同时发生,是指在同一时间点上同时运行多个程序。

五、Java程序运行原理

执行Java命令就会启动Java虚拟机,启动JVM就等于启动了一个应用程序,也就是启动了一个进程,该进程会自动启动一个主线程。然后,主线程会去调用某个类的main方法,所以main方法时运行在主线程中的。

多线程

通过上图,可以看到启动一个Java程序,就会启动一个线程,在该线程中调用main方法。

JVM的启动是多线程的:在运行一个Java程序的时候,在启动主线程的时候,也会启动垃圾回收线程,否则会出现内存溢出。

六、创建线程的方式

1. 继承java.lang.Thread类,重写run()方法,创建线程对象并启动线程。

package com.jd;

/**
 * Created by shifeifei on 2015/8/12.
 */
public class MyThread extends Thread {
    public static void main(String[] args) {
        MyThread t = new MyThread();
        /**
         * run():仅仅是封装了线程执行的代码,直接调用就是普通方法
         * start():首先启动了线程,然后由JVM去调用run()方法
         */
        t.start();

        /**
         *要启动多个线程,必须先创建多个线程对象,再调start()方法
         */
        MyThread t2 = new MyThread();
        t2.start();
    }

    @Override
    public void run() {
        for(int i=0;i<200;i++) {
            System.out.println("当前线程是:"+ this.getName() + " " + i);
        }
    }
}

获取线程名称的方法是:public final String getName(),该方法返回的线程名称形式是:Thread-?(?代表0、1、2等自然数),为什么会是这种形式呢?我们开源码解析:

由于MyThread类继承与Thread类,进入到Thread类,我们可以看到如下代码:

    private static int threadInitNumber;

    public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }

    private static synchronized int nextThreadNum() {
        return threadInitNumber++;
    }
    
    private char    name[];
    
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        this.name = name.toCharArray();
    }
    
    public final String getName() {
        return String.valueOf(name);
    }

即在创建线程对象的时候,就调用init()方法。

既然能够获取线程的名称,我们也能够设置线程的名称,常用的方式有两种:调用public final void setName(String name)和

public Thread(String name)
  • 线程调度

   (1) 分时调度模型:所有线程轮流使用CPU的执行权,平均分配每个线程占用CPU的时间片。

   (2) 抢占式调度模型:优先级高的线程优先使用CPU执行权,如果线程的优先级相同,就会随机选择一个执行,优先级高的线程获取CPU使用权多一些。

   (3) Java中使用抢占式调度模型。

    线程的优先是从1 - 10,默认的优先级为 5.Thread类定义了2个常量:MIN_PRIORITY和MAX_PRIORITY来表示最高和最低优先级。

    1.获取线程的优先级:public final int getPriority()

    2.设置线程的优先级:public final void setPriority(int newPriority)

package com.jd;

/**
 * Created by shifeifei on 2015/8/13.
 * */
public class ThreadPriority extends Thread {
    @Override
    public void run() {
        for(int i=0;i<100;i++) {
            System.out.println(this.getName() + " " + i);
        }
    }

    public static void main(String[] args) {
        ThreadPriority t1 = new ThreadPriority();
        ThreadPriority t2 = new ThreadPriority();
        System.out.println(t1.getPriority() + " " + t2.getPriority());
        t1.start();
        t1.setPriority(10);
        t2.start();
        t2.setPriority(1);
    }
}

3.线程信息

package com.jd;

/**
 * Created by shifeifei on 2015/8/12.
 */
public class MyTestOne {
    public static void main(String[] args) {
        //Thread.currentThread()返回当前线程的引用
        String name = Thread.currentThread().getName();
        int priority = Thread.currentThread().getPriority();
        String groupName = Thread.currentThread().getThreadGroup().getName();

        boolean isDaemon = Thread.currentThread().isDaemon();

        System.out.println("线程名字: " + name);
        System.out.println("线程优先级: " + priority);
        System.out.println("线程组名称: " + groupName);
        System.out.println("是否为守护线程: " + isDaemon);
    }
}
  • 守护线程和用户线程

      1. 守护线程:Java程序运行时后台提供的一种通用服务的线程。守护线程是用来服务用户线程的,如果全部的用户线程结束了,守护线程也就结束了。

package com.jd;

/**
 * Created by shifeifei on 2015/8/12.
 */
public class MyThread extends Thread {
    public static void main(String[] args) {

        Thread t1 = new Thread("daemon") {
            @Override
            public void run() {
                Thread t2 = new Thread("sub") {
                    @Override
                    public void run() {
                        for (int i = 0; i < 20; i++) {
                            System.out.println(this.getName() + " " + i);
                        }
                    }
                };
                t2.setDaemon(true);
                t2.start();
            }
        };
        t1.start();
    }
}

      注意:(1) 所有的用户线程结束时,JVM就可以退出了。

              (2) 设置当前线程为守护线程时必须在start()方法之前。

              (3) 在Daemon线程中产生的新线程也是Daemon的。

              (4) 守护线程应该永远不去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。

  • GroupName,每个线程都会默认在一个线程组里,我们也可以显式的创建线程组,一个线程组中也可以包含子线程组,这样线程和线程组,就构成了一个树状结构。
原文地址:https://www.cnblogs.com/shi-blog/p/4726453.html