多线程(未完)

多线程


线程的基础知识

  • 有多条顺序执行流。可以理解为,一个程序中,有多个main方法在同时执行.

理解线程和进程的区别与联系

  • 进程>线程,一个进程中可以包含多个线程。
  • 进程可以宽泛的理解为一个程序,一个程序即为一个进程(goole,tim)

区分表

线程 进程
并行 并发
短时间内多个线程轮换执行 多个进程同时执行

两种创建线程的方式

1.继承Thread类创建线程

  • 通过该方法创建多线程,每个对象都需要继承Thread类,并且实现run方法
public class FirstThread extends Thread{
    private static int i ;
    @Override
    public void run() {
        for (int i=0;i<20000;i++){
            System.out.println(this.getName() + ":" + i);
        }
    }
    public static void main(String[] args) {
        long l =System.currentTimeMillis();
        // FirstThread f = new FirstThread();
        // f.start();
        // f.start();
        // 会抛出IllegalThreadStateException异常
        /*一个对象不能重复调用start*/
        new FirstThread().start();
        new FirstThread().start();
        //程序正常执行

    }
}

实现Runnable接口创建线程

  • 定义类实现Runnable需要,实现Runable接口,并重写run方法,该run方法的方法体
public class SecondThread implements Runnable {
    private int i = 0;
    @Override
    public void run() {
        for (;i<100;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
    public static void main(String[] args) {
        for (int i = 0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
            if (i==20){
                SecondThread st = new SecondThread();
                new Thread(st,"新线程1").start();
                new Thread(st,"新线程2").start();
            }
        }
    }
}

线程的run()方法和start()方法的区别和联系

  • run()方法仅仅是一个方法,用对象实例调用run方法,仅仅就是在执行一个方法
  • start()方法,则会开辟一条线程

线程的生命周期

  1. 新建
    • 当程序用new新建了一个线程之后,该线程就处于新建状态
  2. 就绪
    • 当线程调用了start方法之后,该线程处于就状态(如果调用run方法,该线程将取消就绪状态,请更换对象调用start)
  3. 运行
    • 处于就绪状态的线程,在获得了cpu处于运算状态的时候,该线程处于运行状态
  4. 阻塞
    • 线程调用sleep方法
    • 调用了一个阻塞式的io方法
    • 线程试图获得一个同步监视器
    • 线程在等待某个通知
    • 程序调用了notify方法将该线程挂起
  5. 死亡
    • run或者call方法执行完成
    • 选不出各部分抛出一个未捕获的exception或者Error
    • 直接调用stop
      线程已死亡,重新start或者,一个新建对象两次start都是错误的

控制线程的常用方法

  • join线程
    • 等待被join的线程执行完毕,请不要在被join线程中调用join,可能会造成锁死
    public static int i =0;
    public void run() {
        for ( ;i<100;i++){
            System.out.println(Thread.currentThread().getName() + " "+i);
            if (i==50){
                try {
                    Thread.currentThread().join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args){
        JoinTest jt = new JoinTest();
        Thread t = new Thread(jt,"1");
        t.setDaemon(true);
        t.start();
        for ( ;i<100;i++){
            System.out.println(Thread.currentThread().getName() + "" +i);
        }
    }
  • sleep,线程睡眠
    • 指定线程暂停
  • yoeld,线程让步
    • 只有在其他线程的优先级,比使用该方法线程的优先级高或者相同,该方法生效
  • 改变线程优先级
    • Thread提供SetPriority、getProiority,来修改线程的优先级(整数1-10)

线程同步的概念和必要性

  • 多个线程修改同一数据,会造成错误
    例子 :x1 ---线程一
    x2 ---线程二
    b ---- a对象的一个方法,操作a对象的值d
    d ---- a对象的一个值----10,这个值必须大于0
    x1,x2同时通过b方法修改d,已知,如果d的值不为10就不能修改
    两个线程的作用相同,将这个值减去10
    x1 x2同时执行

  • 步骤

    1. x1 判断d为10 通过b方法
    2. x2 判断d为10 通过b方法,此时,x1线程还未来得及修改数值造成,x2 同样进入该方法的操作代码中
    3. x1 d-10 = 0 d = 0; 操作a对象的值d成功
    4. x1 线程结束
    5. x2 d-10 = -10 d = -10 操作a对象的值d成功
    6. x2 线程结束

    结果很明了,d=-10,超出正常范围

  • 加锁之后的步骤:

    • 步骤
    1. x1 判断d为10 a对象的该方法上锁
    2. x2 a对象的b方法上锁,线程处于就绪阻塞状态
    3. x1 d-10 = 0 d = 0; 通过a对象的b方法成功操作值为d
    4. x1 线程结束
    5. x2 进入b方法判定 d!=10 x2跳过该方法
    6. x2 线程结束

    综上所知,线程

  • 同步代码块的原理

    • 同步监视器():任何一个时刻,只能有一个线程获得最同步监视器的锁定。其他线程不能访问。
    • 释放同步监视器锁定:在代码块执行完成、抛出未捕获异常或者错误、同步代码块出先break、return、执行了同步监视器的wait方法。
    • 不会释放:sleep、yield、其他线程调用该线程的suspend。

使用synchronized控制线程和同步

  • synchronized块
    synchronized(){

    }
  • synchronized方法
public synchronized void m(){

}

使用Lock对象控制线程同步

private final ReentrantLock lock = new ReentrantLock(); //声明lock锁

使用Object提供的方法实现线程通信

使用管道流实现,线程通信

实现Callable接口实现创建线程

线程池的功能和用法

java8增强的ForkJoinPool

ThreadLocal类功能的用法

使用线程安全的集合类

posted @ 2019-09-01 12:45  zlisang  阅读(99)  评论(0)    收藏  举报