一、进程

进程:是正在运行的程序
是系统进行资源分配和调用的独立单位
每一个进程都有它自己的内存空间和系统资源

二、线程

线程:是进程中的单个顺序控制流,是一条执行路径
单线程:一个进程如果只有一条执行路径,则称为单线程程序
多线程:一个进程如果有多条执行路径,则称为多线程程序。

三、多线程的实现方式

1、方式一:继承Thread类

  1. public
  2. class Thread implements Runnable {

定义一个类继承Thread类
在定义的类中重写run方法
创建定义的类的对象
启动线程

  1. public static void main(String[] args) {
  2. new Thread(new Thread() {
  3. public void run() {
  4. for(int i=0;i<50;i++){
  5. System.out.println("a"+i);
  6. }
  7. }
  8. }).start();
  9. new Thread(new Thread() {
  10. public void run() {
  11. for(int i=0;i<50;i++){
  12. System.out.println("b"+i);
  13. }
  14. }
  15. }).start();
  16. }

2、两个小问题:

为什么要重写run()方法?

因为run()是用来封装被线程指定的代码

run()方法和start()方法的区别?

run():封装线程执行的代码,直接调用,相当于普通方法的调用。
start():启动线程;然后由JVM调用此线程的run()方法

四、设置和获取线程名称:

1、Thread类中设置和获取线程名称的方法

  1. //将此线程的名称更改为等于参数name
  2. public final synchronized void setName(String name) {
  3. checkAccess();
  4. if (name == null) {
  5. throw new NullPointerException("name cannot be null");
  6. }
  7. this.name = name;
  8. if (threadStatus != 0) {
  9. setNativeName(name);
  10. }
  11. }
  12. //返回此线程的名称
  13. public final String getName() {
  14. return name;
  15. }

2、Thread如何命名的源代码

  1. /**
  2. 构造方法里面的名字为Thread-
  3. */
  4. public Thread() {
  5. init(null, null, "Thread-" + nextThreadNum(), 0);
  6. }
  7. //nextThreadNum方法给threadInitNumber++
  8. private static int threadInitNumber;
  9. private static synchronized int nextThreadNum() {
  10. return threadInitNumber++;
  11. //无参构造方法调用此init方法
  12. private void init(ThreadGroup g, Runnable target, String name,
  13. long stackSize) {
  14. init(g, target, name, stackSize, null, true);
  15. }
  16. private void init(ThreadGroup g, Runnable target, String name,
  17. long stackSize, AccessControlContext acc,
  18. boolean inheritThreadLocals) {
  19. if (name == null) {
  20. throw new NullPointerException("name cannot be null");
  21. }
  22. //此name就是threadInitNumbe
  23. this.name = name;
  24. Thread parent = currentThread();
  25. SecurityManager security = System.getSecurityManager();
  26. if (g == null) {
  27. /* Determine if it's an applet or not */
  28. /* If there is a security manager, ask the security manager
  29. what to do. */
  30. if (security != null) {
  31. g = security.getThreadGroup();
  32. }
  33. /* If the security doesn't have a strong opinion of the matter
  34. use the parent thread group. */
  35. if (g == null) {
  36. g = parent.getThreadGroup();
  37. }
  38. }
  39. /* checkAccess regardless of whether or not threadgroup is
  40. explicitly passed in. */
  41. g.checkAccess();
  42. /*
  43. * Do we have the required permissions?
  44. */
  45. if (security != null) {
  46. if (isCCLOverridden(getClass())) {
  47. security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
  48. }
  49. }
  50. g.addUnstarted();
  51. this.group = g;
  52. this.daemon = parent.isDaemon();
  53. this.priority = parent.getPriority();
  54. if (security == null || isCCLOverridden(parent.getClass()))
  55. this.contextClassLoader = parent.getContextClassLoader();
  56. else
  57. this.contextClassLoader = parent.contextClassLoader;
  58. this.inheritedAccessControlContext =
  59. acc != null ? acc : AccessController.getContext();
  60. this.target = target;
  61. setPriority(priority);
  62. if (inheritThreadLocals && parent.inheritableThreadLocals != null)
  63. this.inheritableThreadLocals =
  64. ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
  65. /* Stash the specified stack size in case the VM cares */
  66. this.stackSize = stackSize;
  67. /* Set thread ID */
  68. tid = nextThreadID();
  69. }

3、无参方法设置名字

  1. public static void main(String[] args) {
  2. //继承Thread方法,所以可以使用Thread方法里面的getName
  3. new Thread(new Thread() {
  4. public void run() {
  5. setName("线程aaaa=====");
  6. for(int i=0;i<50;i++){
  7. System.out.println(getName()+":a:"+i);
  8. }
  9. }
  10. }).start();
  11. new Thread(new Thread() {
  12. public void run() {
  13. setName("线程bbbb=====");
  14. for(int i=0;i<50;i++){
  15. System.out.println(getName()+":b:"+i);
  16. }
  17. }
  18. }).start();
  19. }

4、代参构造方法设置名字

  1. public static void main(String[] args) {
  2. //继承Thread方法,所以可以使用Thread方法里面的getName
  3. new Thread(new Thread("线程aaaa") {
  4. public void run() {
  5. for(int i=0;i<50;i++){
  6. System.out.println(getName()+":a:"+i);
  7. }
  8. }
  9. }).start();
  10. new Thread(new Thread("线程bbbb") {
  11. public void run() {
  12. for(int i=0;i<50;i++){
  13. System.out.println(getName()+":b:"+i);
  14. }
  15. }
  16. }).start();
  17. }

5、currentThread():返回对当前正在执行的线程对象的引用

    public static native Thread currentThread();

如何拿到main方法的线程名字

    public static void main(String[] args)  {
        System.out.println(Thread.currentThread().getName());
    }

五、线程调度

1、线程有两种调度模型

分时调度模型:所有的线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间
抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的CPU时间片相对多一些。

2、java使用的是抢占式调度模型:

假如计算机只有一个CPU,那么CPU在某一个时刻只能执行一条指令,线程只有得到CPU时间片,也就是使用权,才可以执行指令。所以说多线程程序的执行是有随机性,因为谁抢到CPU的使用权是不一定的。

3、Thread类中设置和获取线程优先级的方法

//设置优先级
    public final void setPriority(int newPriority) {
        ThreadGroup g;
        checkAccess();
        if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
            throw new IllegalArgumentException();
        }
        if((g = getThreadGroup()) != null) {
            if (newPriority > g.getMaxPriority()) {
                newPriority = g.getMaxPriority();
            }
            setPriority0(priority = newPriority);
        }
    }

//获取优先级
public final int getPriority() {
        return priority;
    }

线程默认优先级是5;
线程优先级的范围是1-10
线程优先级高仅仅表示线程获取的CPU时间片的几率较高

六、线程控制

Thread.java

//使当前正在执行的线程停留(暂停执行)指定的毫秒数    
public static native void sleep(long millis) throws InterruptedException;


//等待这个线程死亡
    public final void join() throws InterruptedException {
        join(0);
    }

//将此线程标记为守护线程,当运行的线程都是守护线程时,java虚拟机将退出
    public final void setDaemon(boolean on) {
        checkAccess();
        if (isAlive()) {
            throw new IllegalThreadStateException();
        }
        daemon = on;
    }
    public static void main(String[] args)  {

        //继承Thread方法,所以可以使用Thread方法里面的getName
        Thread a= new Thread("线程aaaa"){
            public void run() {
                for(int i=0;i<50;i++){
                    try {
                        Thread.sleep(100);  //休眠0.1s
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    System.out.println(getName()+":a:"+i);
                }
            }
        };

        a.start();
        try {
            a.join();  //等待a线程执行完毕,才能往下执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


        new Thread(new Thread("线程bbbb") {

            public void run() {
                for(int i=0;i<50;i++){
                    System.out.println(getName()+":b:"+i);
                }
            }
        }).start();
    }

只有守护线程时虚拟机并不是马上退出,而是等一段时间。

    public static void main(String[] args)  {

        //继承Thread方法,所以可以使用Thread方法里面的getName
        Thread a= new Thread("刘备"){
            public void run() {
                for(int i=0;i<10;i++){
                    try {
                        Thread.sleep(100);  //休眠0.1s
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    System.out.println(getName()+":a:"+i);
                }
            }
        };

        a.start();


        Thread b= new Thread("张飞"){
            public void run() {
                for(int i=0;i<50;i++){
                    try {
                        Thread.sleep(100);  //休眠0.1s
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    System.out.println(getName()+":b:"+i);
                }
            }
        };

        b.setDaemon(true);//设置成守护线程
        b.start();


        Thread c= new Thread("关羽"){
            public void run() {
                for(int i=0;i<50;i++){
                    try {
                        Thread.sleep(100);  //休眠0.1s
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    System.out.println(getName()+":c:"+i);
                }
            }
        };
        c.setDaemon(true);//设置成守护线程
        c.start();
    }

七、线程的生命周期

点击查看【processon】

八、多线程的实现方式

1、方式二:实现Runnable接口

public interface Runnable {

    public abstract void run();
}

定义一个类MyRunnable实现Runnable接口
在MyRunnable类中重写run()方法
创建MyRunnable类的对象
创建Thread类的对象,把MyRunnable对象作为构造方法的参数
启动线程

    public static void main(String[] args)  {

        //如果继承Thread方法,所以可以使用Thread方法里面的getName
       new Thread(new Runnable(){
            public void run() {
                for(int i=0;i<10;i++){
                    try {
                        Thread.sleep(100);  //休眠0.1s
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
//                    System.out.println(getName()+":a:"+i);//因为是Runnable所以没有getName方法
                    System.out.println(Thread.currentThread().getName()+":a:"+i);
                }
            }
        }).start();

        new Thread(new Runnable(){
            public void run() {
                for(int i=0;i<10;i++){
                    try {
                        Thread.sleep(100);  //休眠0.1s
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
//                    System.out.println(getName()+":a:"+i);//因为是Runnable所以没有getName方法
                    System.out.println(Thread.currentThread().getName()+":a:"+i);
                }
            }
        },"第二个参数为线程的名字").start();
    }

相比继承Thread类,实现Runnable接口的好处
避免了java单继承的局限性
适合多个相同程序的代码处理同一个资源的情况,把线程和程序的代码,数据有效分离,较好的体现了面向对象的设计思想