Java学习16 线程

来源:互联网 发布:js 数组初始化0 编辑:程序博客网 时间:2024/06/05 08:40

线程基础

线程是一个程序内部顺序控制流

  • 进程:有独立的代码和数据空间(进程上下文),切换的开销大
  • 线程:轻量的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小
  • 多进程:操作系统同时运行多个任务(程序)
  • 多线程:同一应用程序中有多个顺序流同时执行

线程的概念模型

  • 虚拟的CPU:由java.lang.Thread类封装和虚拟;
  • 代码:传递给Thread类对象执行
  • 数据:传递给Thread类对象处理

创建线程

java.lang.Thread类
run()方法:线程体

方法1:使用Runnable接口创建线程

  1. 定义类实现Runnable接口,重写run()方法;
  2. 创建Thread对象,封装Runnable接口实现类对象;
  3. 调用Thread对象的start()方法。
public class TestThread {    public static void main(String[] args) {        MyRunner r = new MyRunner();        Thread t1 = new Thread(r);        Thread t2 = new Thread(r);        t1.start();        t2.start();    }}class MyRunner implements Runnable {    @Override    public void run() {        for (int i = 0; i < 30; i++) {            String s = Thread.currentThread().getName();            System.out.println(s + ":" + i);        }    }}
  • 多线程共享代码
    多线程之间可以共享代码和数据

    MyRunner r = new MyRunner();Thread t1 = new Thread(r);Thread t2 = new Thread(r);
    线程 虚拟CPU 代码 数据 t1 Thread类对象t1 MyRunner类的run方法 MyRunner类对象r t2 Thread类对象t2 MyRunner类的run方法 MyRunner类对象r

方法2:直接继承Thread类创建线程

  1. 继承Thread类,重写run()方法
  2. 创建Thread子类对象
  3. 调用对象start()方法
这里写代码片

两种方式比较:

  1. 使用Runnable接口创建线程:

    • CPU/代码/数据分开,模型清晰
    • 线程体run()方法所在类还可以继承其他类
    • 有利于保持程序风格一致性
  2. 直接继承Thread类创建线程:

    • Thread子类不能在继承其他类
    • 编写简单,run()方法当前对象就是线程对象,可直接操纵

后台线程/守护线程/精灵线程

  • 后台处理(Background Processing)
  • 后台线程/守护线程/精灵线程(Background Thread/Daemon Thread):如垃圾回收线程、定时器线程
  • 用户线程(User Thread)
  • 主线程(Main Thread)
  • 子线程(Sub Thread)

方法:
boolean isDaemon()
void setDaemon(Boolean on)

注意:当一个应用程序的所有用户线程都执行结束后,JVM都会停止后台线程的执行(当可能存在延时)。

GUI线程

常见GUI线程
- AWT-Windows线程:操作系统获取底层事件信息
- AWT-EventQueue-n线程:GUI事件处理队列,从AWT-Windows线程获取事件消息
- AWT-Shutdown线程:负责关闭已经打开的抽象窗口工具,释放GUI所有占用的资源,等其他线程退出后才执行

其他非GUI线程:
- DestoryJavaVM:释放所占用的系统资源,卸载JVM,清除内存class和内存空间。在主线程main执行结束后被拉起

线程控制

线程生命周期

  • 新建:start()进入就绪状态
  • 就绪:线程调度器调度后进入运行状态
  • 运行:阻塞事件(如IO读取)进入阻塞状态;run()执行完毕进入终止状态
  • 阻塞:阻塞解除进入就绪状态
  • 终止:终止后不能重新start()

线程优先级

  • 范围1~10,缺省为5
  • 子线程优先级默认与父线程相同

方法:
int getPriority()
void setPriority(int newPriority)

静态常量:
Thread.MIN_PRIORITY=1
Thread.MAX_PRIORITY=10
Thread.NORM_PRIORITY=5

线程控制中,不能依赖线程优先级,来决定线程执行的先后顺序

线程串行化

线程运行过程中,依赖另一线程的运行结果(或等待运行一段时间),进入阻塞状态

  • final void join()
  • final void join(long millis)
  • final void join(long millis, int nanos)

线程休眠

  • static void sleep(long millis)
  • static void sleep(long millis, int nanos)

线程让步

让运行中的线程主动放弃当前CPU处理机会;但不是使线程阻塞,而是转入就绪状态

  • static void yield()

线程的挂起和恢复

  • final void suspend()
  • final void resume()

终止线程

  • 不提倡使用Thread下stop()方法

线程控制基本方法

方法 功能 isAlive() getPriority() setPriority() sleep() join() yield() suspend()/resume() wait() 当前线程进入对象的wait pool noitfy()/notifyAll() 唤醒对象的wait pool中的一个/所有的等待线程

线程的同步

临界资源问题

原因是对共享数据访问操作的不完整性

互斥锁

  • 保证共享数据操作的完整性
  • 每个对象都对应有一个可称为“互斥锁”的标记,保证在任一时刻,只能有一个对象访问该对象

  • 关键字synchronized:表明对象在任一时刻,只能有一个线程访问

    • 两种用法:
      方法声明(同步方法):

      public synchronized void push(char c) { /*...*/ }

      修饰语句块(同步块):

      public char pop(){    synchronized(this){ /*...*/ }}

线程死锁

举例:

这里写代码片

线程同步通讯

  • 为避免死锁,在线程进入阻塞状态时,应尽量释放其锁定的思源,以为其他的线程提供运行的机会

    • final void wait()
    • final void notify()
    • final void notifyAll()
  • 运行态线程调用对象的wait()方法:① 线程进入阻塞状态(阻塞在对象wait()池中);②解除调用者对象的锁定状态。
    其他线程调用同一对象notify()/notifyAll()方法:获取对象锁后,进入就绪状态。

  • suspend()/resume()不同,其不会释放资源

// 生产者-消费者问题这里写代码片

多线程编程专题

线程见数据传输

PipeInputStream/PipeOutputStream

  • 管道流
    • 不提倡在单个线程中使用管道流,而是在多个线程中使用
    • 一般也不在高并发环境下使用,避免死锁

类的同步性与线程安全

  • Vector(同步安全) vs. ArrayList(非同步安全)

分析:Vector是安全的,但size()和elementAt()方法调用存在状态依赖
修改:

定时器

  • Timer
    • schedule方法
  • TimerTask
原创粉丝点击