线程的6种状态:
1.New(可创建)
2.Runnable(可运行)
3.Blocked(被阻塞)
4.Waiting(等待)
5.Timed waiting(计时等待)
6.Terminated(被终止)
1.新创建线程:当用new 操作符创建一个新线程时,如New Thread(r),该线程还没有开始运行。这意味着它的状态是new。当一个线程处于新创建状态时,程序还没有开始运行线程中的代码。
2.可运行线程:一旦调用start方法,线程处于runnable状态。一个可运行的线程可能正在运行也可能没有运行,这取决于操作系统给线程提供运行的时间。
一旦一个线程开始运行。它不必始终保持运行。事实上,运行中的线程被中断,目的是为了让其他线程获得运行机会。
3.被阻塞线程和等待线程:当线程处于阻塞或等待状态时,它暂时不活动。它不运行任何代码且消耗最少的资源。直到线程调度器重新激活它。细节决定于它是怎么达到非活动状态的:
当一个线程尝试获取一个对象的内部锁,而该锁被其他线程持有,则该线程进入阻塞状态。当所有其他线程释放该锁,并且线程调度器允许本线程持有它的时候,该线程变成非阻塞状态。
当线程等待另一个线程通知调度器一个条件时,它自己进入等待状态。
有几个方法有一个超时参数。调用它们导致线程进入计时等待状态。这一状态将一直保持到超时期满或者接受适当的通知。带有超时参数的方法有Thread.sleep 和Object.wait、Thread.join、Lock.tryLock以及Condition.await的计时版。
4.被终止的线程:
线程因一下两个原因之一而被终止:
- 因为run方法没有正常退出而自然死亡。
- 因为一个没有捕获的异常终止了run方法而意外死亡。
中断线程:
当对一个线程调用interrupt方法时,线程的中断状态将被置位。这是每一个线程都具有的boolean标志。每个线程都应该不时地检查这个标志,以判断线程是否被中断。
要想弄清中断状态是否被置位,首先调用静态的Thread.currentThread方法获得当前线程,然后调用isInterrupted方法。
while(!Thread.currentThread().isInterrupted()&& more work to do){
do more work
}
但是,如果线程被阻塞,就无法检测中断状态。这是产生InerruptedException异常的地方。
线程优先级:
每个线程都有一个优先级。默认情况下,一个线程继承它的父线程的优先级。可以用setPriority方法提高或降低任何一个线程的优先级。可以将优先级设置为在MIN_PRIORITY(在Thread类中定义为1)与MAX_PRIORITY(定义为10)之间的任何值。NORM_PRIORITY定义为5.
每当线程调度器有机会选择新线程时,它首先选择具有较高优先级的线程。
守护线程:
可以通过调用
Thread.setDaemon(true);
将线程转换为守护线程。守护线程的用途是为其他线程提供服务。
Java多线程实现的三种方式:
1、继承Thread类;
2、实现Runnable接口;
3、使用ExecutorService、Callable、Future实现有返回结果的多线程。
1.通过扩展Thread类来创建多线程
demo:
public class MyThread extends Thread {
private static int a=1;
public void run() {
a++;
System.out.println(a);
}
public static void main(String[] args) {
MyThread thread1=new MyThread();
thread1.start();
MyThread thread2=new MyThread();
thread2.start();
}
}
2.通过实现Runnable接口来创建多线程
public class MyRunnable implements Runnable{
private static int a=1;
public void run() {
a++;
System.out.println(a);
}
public static void main(String[] args) {
MyRunnable runnable1=new MyRunnable();
//实例化一个Thread,并传入自己的Runnable实例
Thread thread1=new Thread(runnable1);
MyRunnable runnable2=new MyRunnable();
Thread thread2=new Thread(runnable2);
thread1.start();
thread2.start();
}
}
3.使用Callable、Future实现有返回结果的多线程
public class MyCallable implements Callable<Integer>{
private static int a=1;
public Integer call() {
a++;
System.out.println(a);
return 1;
}
public static void main(String[] args) {
MyCallable counter=new MyCallable ();
FutureTask<Integer> task=new FutureTask<Integer>(counter);
//实例化一个Thread,并传入自己的FutureTask实例
Thread thread=new Thread(task);
thread.start();
}
}
线程锁:
用ReentrantLock保护代码块的demo:
class C {
//锁对象
private final ReentrantLock lock = new ReentrantLock();
......
//保证线程安全方法
public void method() {
//上锁
lock.lock();
try {
//保证线程安全操作代码
} catch() {
} finally {
lock.unlock();//释放锁
}
}
}
public sychronized void method(){
method body
}
等价于加锁
demo:
public class Bank {
private static int[] amount={1000,1000};
private final ReentrantLock lock = new ReentrantLock();
public void transfer(){
//上锁
lock.lock();
try {
amount[0]-=1000;
amount[1]+=1000;
System.out.println("totla="+(amount[0]+amount[1]));
} finally {
lock.unlock();//释放锁
}
}
}
public class MyThread extends Thread {
private static int a=0;
private static int b=0;
private Bank bank;
public MyThread(Bank bank){
this.bank=bank;
}
public void run() {
this.bank.transfer();
}
public static void main(String[] args) {
Bank b=new Bank();
for(int i=1;i<=10000;i++){
MyThread thread=new MyThread(b);
thread.start();
}
}
}
线程条件对象:
一个锁对象可以有一个或多个相关的条件对象。可以用newCondition方法获得一个条件对象。
demo(设置一个条件对象来表达“余额充足”条件):
public class Bank {
private Condition sufficentFunds;
....
public Bank(){
sufficentFunds=lock .newCondition();
}
public void transfer(){
lock .lock();
try{
while(account(amount[0]-1000<0){
//wait
...
}
//transfer fund
....
}finally{
lock.unlock();
}
}
}
如果transfer方法发现余额不足,它调用
sufficentFunds.await();
当前线程被阻塞,并且放弃了锁。
等待获得锁的线程和调用await()方法的线程存在本质的不同。一旦一个线程调用await方法,它进入该条件的等待集。当锁可用时,该线程不能马上解除阻塞。必须另一个线程调用signallAll()方法时为止。
线程等待与激活demo:
public class Bank {
private static int[] amount={1000,1000};
private final ReentrantLock lock = new ReentrantLock();
public synchronized void transfer() throws InterruptedException{
Random random=new Random();
int i=random.nextInt(2);
int j=random.nextInt(2);
while(amount[i]<1000){
wait(); //当余额不足时,该线程进入等待状态,必须另一线程调用notify方法激活才能解除阻塞
}
amount[i]-=1000;
amount[j]+=1000;
notifyAll();//解除所有等待线程的阻塞,以便这些线程通过竞争实现对对象的访问
System.out.println("totla="+(amount[0]+amount[1]));
}
}
public class MyThread extends Thread {
private static int a=0;
private static int b=0;
private Bank bank;
public MyThread(Bank bank){
this.bank=bank;
}
public void run() {
try {
this.bank.transfer();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
Bank b=new Bank();
for(int i=1;i<=10000;i++){
MyThread thread=new MyThread(b);
thread.start();
}
}
}
同步阻塞:
每个java对象有一个锁。线程获得锁有两种方法:
1.通过调用同步方法获得锁
2.通过进入一个同步阻塞获得锁,demo:
synchronized(obj){
cirtical section
}
获得obj对象的锁
public class Bank{
private double[] accounts;
private Object lock=new Object();
...
public void transfer(){
synchronized(lock){
...
}
}
}
Volatile关键字:
有时,仅仅为了读写一个或俩个实例就使用同步,显得开销过大了。volatile关键字为实例域的同步访问提供了一种免锁机制。如果声明一个域为volatile,那么编译器和虚拟机就知道该域是可能被另一个线程并发更新的。
死锁:
所有线程都被阻塞。
读写锁:
ReentrantLock类和ReentrantReadWriteLock类
下面是使用读/写锁的必要步骤:
1. 构造一个ReentrantReadWriteLock 对象
private ReentrantReadWriteLock rwl=new ReentrantReadWriteLock ();
2.抽取读锁和写锁
private Lock readLock=rwl.readLock();
private Lock writeLock=rwl.writeLock();
3.对所有的获取方法加读锁:
public void get(){
readLock.lock();
try{
....
}finally{
readLock.unlock();
}
}
4.对所有的修改方法加读锁:
public void modify(){
writeLock.lock();
try{
....
}finally{
readLock.unlock();
}
}
阻塞队列:
在协调多个线程之间的合作时,阻塞队列是一个有用的工具。工作者线程可以周期性将中间结果存储在阻塞队列中。其他的工作者线程移出中间结果并进一步加以修改。
java.until.concurrent提供了阻塞队列的几个变种:
1.LinkedBlockingQueue (双端,容量没有上限,但可以指定容量)
2.ArrayBlockingQueue (在构造时需要指定容量,并且有一个可选参数来指定是否需要公平性)
3.PriorityBlockingQueue(带优先级的队列)
demo:
public class BlockingQueueTest {
public static void main(String[] args) {
Scanner in=new Scanner(System.in);
System.out.print("Enter 文件路径:");
String directory=in.nextLine();
System.out.print("Enter 关键字:");
String keyword=in.nextLine();
final int FILE_QUEUE_SIZE=10;
final int SEARCH_THREADS=100;
BlockingQueue<File> queue=new ArrayBlockingQueue<File>(FILE_QUEUE_SIZE);
FileEnumerationTask enumeration=new FileEnumerationTask(queue, new File(directory));
new Thread(enumeration).start();
for(int i=1;i<SEARCH_THREADS;i++){
new Thread(new SearchTask(queue, keyword)).start();
}
}
}
public class FileEnumerationTask implements Runnable {
public static File DUMMY=new File("");
private BlockingQueue<File> queue;
private File startingDirectory;
public FileEnumerationTask(BlockingQueue<File> queue, File startingDirectory) {
super();
this.queue = queue;
this.startingDirectory = startingDirectory;
}
@Override
public void run() {
try {
enumerate(startingDirectory);
queue.put(DUMMY);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void enumerate(File directory) throws InterruptedException{
File[] files= directory.listFiles();
for(File f:files){
if(f.isDirectory()) enumerate(f);
else queue.put(f);
}
}
}
public class SearchTask implements Runnable{
private BlockingQueue<File> queue;
private String keyword;
public SearchTask(BlockingQueue<File> queue, String keyword) {
super();
this.queue = queue;
this.keyword = keyword;
}
@Override
public void run() {
try {
boolean done=false;
while(!done){
File file = queue.take();
if(file==FileEnumerationTask.DUMMY){
queue.put(file);
done=true;
}
else
search(file);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void search(File file){
try {
Scanner in=new Scanner(file);
int lineNumber=0;
while(in.hasNextLine()){
lineNumber++;
String line=in.nextLine();
if(line.contains(keyword)){
System.out.printf("%s:%d:%s%n",file.getPath(),lineNumber,line);
}
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
线程安全的集合
java.until.concurrent包提供了映射表、有序集和队列的高效实现:
ConcurreentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet和ConcurrentLinkedQueue
从Java的初始版本开始,V和HashTable类就提供了线程安全的动态数组和散列表的实现。现在这些类被弃用,取而代之的是ArrayList和HashMap类。这些类不是线程安全的,而集合库中提供了不同的机制,任何集合类通过使用同步包装器变成线程安全的:
List<E> synchAttayList=Collections.synchronizedList(new ArrayList<E>());
Map<K,V> synchHashMap=Collections.synchronizedMap(new HashMa<K,V>());
线程池
执行器(Executor)类有许多静态工厂方法用来构建线程池,