《Java多线程编程核心技术》

线程

Thread

Java实现多线程编程有两种方式,一种是继承Thread类,重写run()方法;另一种是实现Runnable接口,实现run()方法。其中,Thread类实现了Runnable接口,由于Java不支持多继承,因此实际编程时多用实现Runnable接口的方式来创建多线程。

  • currentThread()方法:可返回代码段正在被哪个线程调用的信息。
1
Thread.currentThread().getName(); //返回当前线程的名称
  • boolean isAlive()方法:判断当前的线程是否处于活动状态

  • sleep(long millis)方法:在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行)。

  • long getId()方法:取得线程的唯一标识。

  • interrupt()方法:停止线程,但不是立即停止,仅仅是在当前线程中打了一个停止的标记,并非真的停止线程。

    • boolean this.interrupted():测试当前线程是否已经中断。该方法具有清除状态的作用,连续两次调用,第二次返回false。

    • boolean this.isInterrupted():测试线程是否已经中断。

  • stop()方法可以停止线程,suspend()方法可以暂停线程,resume()方法可以恢复线程。这三个方法存在线程安全问题,已经被JDK标明过期作废的方法。

  • void yield()方法:使线程放弃当前的CPU资源,将它让给其他的任务去占用CPU执行时间。但放弃时间不确定,有可能刚刚放弃又马上获得。

  • void setPriority(int newPriority)方法:可以设定线程的优先级,优先级为1~10,正常值为5,值越大优先级越高。


Synchronized

  • 多线程并发访问同一个对象的多个实例时可能出现交叉问题;访问同一个对象的同一个实例时会造成值覆盖。

  • “非线程安全”问题存在于“实例变量”中,如果是方法内部的私有变量,则不存在“非线程安全”问题。

  • synchronized()方法:线程会对加有synchronized关键字的方法或代码块进行同步访问。

    当A线程调用anyObject对象加入synchronized关键字的X方法时,A线程就获得了X方法锁,更准确讲,是获得了对象的锁,其他线程必须等到A线程执行完毕才可以调用X方法,但B线程可以随意调用其他的非synchronized方法

    • 一个synchronized方法/块的内部调用本类的其他synchronized方法/块,是永远可以得到锁的。

    • 出现异常时,锁自动释放。

    • 同步不可以继承。

    • synchronized(x){}代码块支持将任意对象作为锁。

      • 当多个线程同步执行synchronized(x){}同步代码块时呈同步效果。

      • 当其他线程执行x对象中synchronized同步方法时呈同步效果。

      • 当其他线程执行x对象方法里面的synchronized(this){}代码块时也呈现同步效果。

    • 关键字synchronized加在static静态方法上,是对当前的*.java文件对应的Class类进行持锁。

  • volatile关键字:使变量在多个线程间可见。

    • 关键字volatile是线程同步的轻量级实现,只能修饰变量,性能比synchronized好。

    • 多线程访问volatile不会发生阻塞,synchronized会出现阻塞。

    • volatile能保证数据的可见性,但不能保证原子性。synchronized既可以保证可见性,又可以保证原子性。

    • volatile解决的是变量在多个线程之间的可见性,synchronized解决的是多个线程之间访问资源的同步性。


线程间通信

wait / nofity (等待/通知) 机制

  • wait()方法:使当前执行代码的线程进行等待。

  • wait(long):使线程等待一段时间,在该时段内可以被唤醒,如果超过这个时间没被唤醒则自动唤醒。

    在调用wait()之前,线程必须获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait()方法。在执行wait()方法后,当前线程释放锁。在从wait()返回前,线程与其他线程竞争重新获得锁。如果调用wait()时没有持有适当的锁,则抛出IllegalMonitorStateException,它是RuntimeException的一个子类,不需要try-catch语句进行捕捉。

  • nofity()方法:由线程规划器随即挑选出其中一个呈wait状态的线程,使该线程等待获取该对象的对象锁。

    notify()在调用前,线程也必须获得该对象的对象锁,即只能在同步方法或同步块中才能调用notify()方法。如果调用notify()时没有持有适当的锁,也会抛出IllegalMonitorStateException异常。执行notify()方法的线程,在执行完notify()方法后不会马上释放该对象锁,要等到该线程退出同步代码块后才会释放锁,这时被通知的线程才能获取锁。

  • notifyAll()方法:使所有正在等待队列中等待同一共享资源的“全部”线程从等待状态退出,进入可运行状态。

  • join()方法:使主线程等待子线程的终止。也就是子线程调用了join()方法后面的代码,只有等子线程结束了才能执行。

    • join(long):设定等待的时间。

threadStatus

ThreadLocal

  • ThreadLocal:隔离线程间的共享数据,使每个线程可以拥有自己的私有数据。

    • get():取出ThreadLocal中的私有数据。

    • set():将私有数据放入ThreadLocal中。

  • InheritableThreadLocal:可以在子线程中取得父线程中继承下来的值。

Lock

  • ReentrantLock():ReentrantLock类实现了Lock,它拥有synchronized相同的并发性和内存语义,但是添加了类似锁投票、定时锁等候和中断锁等候的一些特性。

    • Lock.lock():给对象加锁。

    • Lock.unlock():释放锁。

  • Condition():条件或条件队列。能够提供使线程等待或唤醒的功能。

    • Condition.await():使线程等待,相当于wait()方法。

    • Condition.await(long time,TimeUnit unit):使线程等待一段时间,超时则自动唤醒,相当于wait(long timeout)方法;

    • Condition.singal():唤醒等待线程,相当于notify()方法。

    • Condition.singalAll():唤醒所有等待线程,相当于notifyAll()方法。

  • 公平锁与非公平锁

    锁Lock分为“公平锁”和“非公平锁”,公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即先到先得的FIFO先进先出顺序。而非公平锁就是一种获取锁的抢占机制,是随即获得锁的,和公平锁不一样的就是先来的不一定先得到锁,这个方式可能造成某些线程一直拿不到锁,结果也就是不公平的了。

    • ReentrantLock(boolean fair):通过参数fair确定该锁是否为公平锁。true为公平锁,false为非公平锁。
  • int getHoldCount():查询当前线程保持此锁定的个数,也就是调用lock()方法的次数。

  • int getQueueLength();返回正等待获取此锁定的线程估计数。

  • int getWaitQueueLength(Condition condition):返回等待与此锁定相关的给定条件Condition的线程估计数。

  • boolean hasQueuedThread(Thread thread):查询指定的线程是否正在等待获取此锁定。

  • boolean hasQueuedThreads():查询是否有线程正在等待获取此锁定。

  • boolean hasWaiters(Condition condition):查询是否有线程正在等待与此锁定有关的condition条件。

  • boolean isFair():判断是否是公平锁。

  • boolean isHeldByCurrentThread():查询当前线程是否保持此锁定。

  • boolean isLocked():查询此锁定是否由任意线程保持。

  • void lockInterruptibly():如果当前线程未被中断,则获取锁定,如果已经被中断则出现异常。

  • boolean tryLock():仅在调用时该锁未被另一个线程保持的情况下,才获取此锁定。

  • boolean tryLock(long timeout,TimeUnit unit):如果锁定在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
例:
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();

lock.getHoldCount();
lock.getQueueLength();
lock.getWaitQueueLength(condition);

lock.hasQueuedThread(threadx);
lock.hasQueuedThreads();
lock.hasWaiters(condition);

lock.isFair();
lock.isHeldByCurrentThread();
lock.isLocked();

ReentrantReadWriteLock

读写锁表示有连个锁,一个是读操作相关的锁,也称为共享锁;另一个是写操作相关的锁,也叫做排他锁。多个读锁之间不互斥,读锁与写锁互斥,写锁与写锁互斥

1
2
3
4
例:
ReentrantReanWriteLock lock = new ReentrantReenWriteLock();
lock.readLock().lock();
lock.writeLock().lock();

ReentrantLock和Condition详解


线程的状态

  • 任何线程一般具有五种状态,即创建、就绪、运行、阻塞、终止。线程状态的转移与方法之间的关系如图:

threadStatus