Java并发笔记1

进程和线程

一个进程可以有多个线程,线程有自己的堆栈,自己的程序计数器和局部变量,但是他们共享进程的
系统资源。因此要小心线程之间彼此影响。

并发编程三个特点:

  • 原子性问题
  • 可见性问题
  • 有序性问题

多线程优点

  • 进程间内存不能共享,而线程间可以共享内存
  • 新建进程需要分配系统资源,而新建线程则不需要分配,创建和切换代价小,从而使多线程来实现
    多任务比多进程效率更高。

java线程的创建

  1. 使用Thread()创建线程,使用start()方法来启动线程。
  2. 也可以通过实现Runnable接口,然后作为Thread的target创建线程。
  3. Runnable接口方式的优点是可以继承其他类。

线程的周期

  1. 创建。想让新创建的子线程立即执行可以使用Thread.sleep(1),让主线程暂停一下。
  2. 就绪
  3. 运行
  4. 阻塞
  5. 死亡

tip

  1. 注意少使用suspend()stop(),这样会造成死锁。
  2. 使用isAlive()判断

线程的控制

  1. join线程
    在执行的线程A运行时调用其他线程B的B.join()方法后,A线程将等到B线程结束之后才执行。
  2. 后台进程
    start()之前,调用setDaemon()。可以使用isDaemon()判断是否是后台线程,后台线程
    创建的线程默认是后台线程。程序中的所有前台线程运行结束后,后台线程会被通知死亡,不过这会
    有一定的时间。
  3. yield()静态方法
    sleep()类似,但是不会让位于优先级比自己低的线程,所以如果其优先级最高且没有同级的
    线程的话,调用此方法后该线程依然继续执行。
  4. 设置线程优先级
    setPriority(int)接受1~10的整数,或者三个静态常量: MAX_PRIORITY,MIN_PRIORITY,NORM_PRIORITY,对应的数值依次为10,1,5.
    虽然Java提供了1~10共10个级别,但是有的系统并不支持,所以为了移植性,最佳实践是
    使用常量而为直接指定数值

线程同步

同步代码块

eg. synchronized(obj),其中obj为同步监视器,Java允许使用任何对象作为同步监视器,
但是由于我们的目的是阻止两条线程并发访问一个共享资源,所以最佳实践是使用可能被并发访问
的资源作为同步监视器。

同步方法

此时不需要指定同步监视器,方法会自动把this当成同步监视器。通过同步方法,一般可以使类
成为线程安全的类,这样的类有如下特点:

  1. 该类的对象可以被多个线程同时访问
  2. 访问该对象的任意方法都能得到正确的结果
  3. 访问该对象的任意方法,该对象都能依然保持合理状态

可变类的线程安全是以降低程序效率为代价的,为了降低对性能的影响,需要采取以下策略:

  1. 减少同步的使用,只对会改变竞争资源的方法同步
  2. 如果可变类有两个运行环境:多线程环境和单线程环境,则可以开发线程安全和非线程安全两个
    版本
释放同步监视器的锁定

程序无法显示释放对同步监视器的锁定,线程释放锁定有以下几种情况:

  1. 同步方法或者代码块执行完毕
  2. 同步方法执行到break,return
  3. 同步方法执行到wait()方法
  4. 同步方法遇到ErrorException等中止程序继续运行的情况
    线程不会释放锁定的情况:
  5. 执行sleep(),yield()
  6. 执行suspend()
同步锁Lock

类似synchronized的机制,Lock常用的是ReentrantLock。同步锁需要显示的加锁和释放。
通常在finally里释放。ReentrantLock具有重入性,线程可以对它已经加锁的ReentrantLock
再次加锁,所以一段被锁保护的方法可以调用另一个被相同锁保护的方法

死锁

少使用suspend()方法,会造成死锁。

线程通信
  1. synchronized方式
    • wait() :导致该线程等待并放弃同步监视器,知道该同步监视器的notify()或者notifyAll()方法来唤醒改线程
    • notify() :选择任意一个在该同步监视器上的线程,将其唤醒
    • notifyAll() :唤醒所有等待同步监视器的线程
    • 上述三个方法都属于Object而非Thread,这三个方法都必须由同步监视器来调用。
  2. 同步锁方式
    • 如果不使用synchronized而是用Lock的话则不能再使用上述方法,此时我们使用Java提供的Condition类保持协调。Lock代替了同步方法或者同步块,Condition代替了同步监视器的功能。Condition提供了类似的三个方法:
    • await() : await()还有多种变体如: awaitNanos(long), awaitUninterruptibly()
    • signal()
    • signalAll()
  3. 使用管道流
    与普通IO类似,可以使用字节流、字符流和新IO Channel三种形式。通常有共享数据就可以了,不需要使用管道流。