多线程学习(一)

前言

在讲多线程之前,是需要先理解,线程和进程. 那么可以看看这篇文章:http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html
概念字典的建立:
* 多线程:程序运行时,产生多个线程,多线程是为了同步完成多项任务,不是为了提高运行效率.仅仅是提供资源使用率
* 并行:多个cpu实例或机器同时执行一段处理逻辑
* 并发:cpu调度,看起来同时执行
c-concurrent p-parallel
这里写图片描述

线程状态

在看Thread的源码时,里面有一个静态枚举类State.定义了六种状态: NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED
这里写图片描述

新建

使用new关键字和Thread类或其子类建立一个线程对象后,这个线程对象就处于新建状态,此时线程对象,就初始化完成了,在堆内存中等待着运行

运行

上面新建之后,调用start(),就可以启动这个线程,如果此时获取了CPU资源,就可以执行run(), 此时就是处于运行状态了

blocked VS waiting

blocked: 一个线程试图获取一个内部的对象锁
waiting:一个线程等待另一个线程完成某个操作

sleep VS wait

sleep,使线程睡眠一段指定时间,进入timed_waiting状态,超时后,重新进入runnable状态.
sleep会保留当前线程运行状态,线程所持有的锁资源不会释放
wait,会释放线程持有的锁资源.

interrupt

很多时候调用它,不是为了停止线程,而是为了让线程继续运行下去
* 如果线程处于等待状态或者阻塞在某个资源上,interrupt()使线程跳出这种状态继续执行
* 如果线程正常执行,interrupt(),设置了线程的中断状态

yield VS sleep

yield,让出当前线程占用的CPU资源
sleep() :线程进入waiting,等待超时时间到达后再次执行
yield(): 从running进入ready状态,该方法经常用不到

先建线程

继承Thread类

  • 定义一个类继承Thread类
  • 重写run()
  • run()中编写需要执行的步骤,方法执行体
  • main(),创建线程对象,启动线程
A a=new A();
a.start();
  • 线程只能启动一次

实现Runnable接口

*定义一个类实现Runnable接口
* 覆盖run()
* run()编写需要执行的步骤,方法执行体
* main()创建线程对象,启动线程

Thread t=new Thread(new A());
t.start();

实现Callable接口

  • 实现Callable接口,相较于实现Runnable接口,方法可以有返回值,且可以抛出异常
  • 执行Callable,需要FutureTask实现类的支持,用于接收运算结果

Callable()与Runnable()对比

  • Callable规定的方法是call(), Runnable规定的是run()
  • Callable的任务执行后可返回值, Runnable的任务是不能返回值的
  • call()可抛出异常, run()不能抛出异常

Thread和Runnable对比

  • 如果一个类继承Thread,不适合资源共享,实现runnable接口,容易实现资源共享
  • 实现接口, 适合多个相同程序代码线程处理同一个资源; 避免java中单继承限制; 增加程序的健壮性,代码可以被多个线程共享,代码和数据独立; 线程池只能放入实现Runnable或callable类线程,不能直接放入继承Thread类

线程优先级

每个线程都有优先级,更高优先级的线程优先运行,优先意思:分配cpu时间段,得到的概率高一些. 所以并不是优先级越高,就越早完成
可通过Thread.setProority设置线程优先级.优先级一共10个级别,默认为5

线程同步

简单理解:排队;
当多个线程访问一个数据对象,可能对数据会造成破坏,所以同一时间,只运行一个线程执行,其他线程不能参与执行

synchronized

  • 整个方法体,都成为了同步执行状态–同步方法;
  • 使用方式
synchronized public void addI(String username){
}
  • 针对需要同步的代码–同步方法块
  • 使用方式
synchronized(obj){
}

Lock

ReentrantLock

  • 完全互斥排他锁
private Lock lock=new ReentrantLock();
    public void testMethod(){
        lock.lock();
        for (int i = 0; i <5 ; i++) {
            System.out.println("ThreadName="+Thread.currentThread().getName()+(" " +(i+1)));
        }
        lock.unlock();
    }

volatile

  • 具备synchronized的可见性,不具备原子性,保证共享变量的”可见性”,可见性:当一个线程修改一个共享变量时,并发的每个线程总能看到其他线程对这个volatile变量最后的写入.
  • 线程安全情况下, 加volatile会牺牲性能
volatile boolean on=true;

小结

  • 获取的锁是对象锁,而不是把一段代码或方法(函数)当成锁
  • 每个对象只有一个锁与之相关联

线程通信

线程间能够互相发送信号,例如: 线程B可以等待线程A的一个信号,这个信号会通知线程B数据已经准备好.
* 通过共享对象通信:synchronized
* 尽管两个线程之间,需要调用的是不同方法,但是是同步执行的.
* 等待/通知机制
* 一个线程修改了一个对象的值,另一个线程感知到变化, 进行相应的操作,整个过程开始于一个线程,最终执行又是一个线程.前者是生产者,后者是消费者. 等待/通知相关方法是任意java对象都具备的.
* 一个线程A调用了对象O的wait()进入等待状态,线程B调用对象O的notify()或notifyAll(),对象上的wait())和notifyAll()像开关信号一样,完成等待放和通知方交互工作
* 等待/通知机制依托于同步机制,确保等待线程从wait()返回时能够感知到通知线程对变量做出的修改
* 管道输入/输出流
* 主要用于线程之间的数据传输, 传输媒介是内存
* Thread.join()
* 当前线程A等待thread线程终止之后,才从thread.join()返回

高级线程控制类

ThreadLocal

线程变量,以ThreadLocal对象为键,任意对象为值的存储结果; 当使用ThreadLocal维护变量时,为每个使用该变量的线程提供独立的变量副本.所以每个线程可以独自改变自己的副本,不会影响其他线程所对应的副本.

InheritableThreadLocal

  • 可以在子线程中取得父线程继承下来的值
  • 值继承再修改

Lock

  • ReentrantLock
  • ReentrantReadWriteLock
    上面有讲,主要为了解决同步问题
    锁这一部分,会单独拿出来讲,这里就不单独来说了

猜你喜欢

转载自blog.csdn.net/kwy15732621629/article/details/80749942