1.线程:它是一个并发执行的顺序流,一个进程包括多个顺序执行流程,这个执行流程称为线程。
线程是由操作系统创建并维护的一个资源,JVM就是一个进程。对于单个CPU来说。某个时刻只有一个线程在运行。
线程由CPU分配给线程的时间片、线程代码、线程数据三个部分组成。
线程与进程区别:进程是独立的数据空间,线程是共享的数据空间。//main也是一个线程为主线程。
注意:a.线程和线程对象时两码事,线程对象能够到底层去申请一个线程资源。
b.进程的调度室由OS负责。
2.线程的两种实现方式:实现Runnable接口和继承Thread类。
第一种:
class MyThread extends Thread{
public void run(){//需要执行的代码}
}
public class TestThread{
public static void main(String[] args){Thread t=new MyThread(); t.start();}
}
//只有等到所有的线程结束,进程才结束。
第二种:
class MyRunnable implements Runnable{
public void run(){(){//需要执行的代码}}
} public class TestThread{
public static void main(String[] args){
Runnable r = new MyRunnable();
Thread t1 = new Thread(r); t1.start();
}}
3.线程中断
jdk1.0中存在一个stop方法,其他线程可以调用它来终止线程,不过现在已经被抛弃,现在可以用interrupt方法来请求终止一个线程。当interrupt方法被调用时,该线程的中断状态将会被置位。
我们可以首先调用静态的Thread.currentThread方法取得当前线程,然后调用它的isInterrupted方法判断中断状态是否被置位了。
若一个线程被阻塞了,它就无法检查中断状态,这就是产生InterruptedException异常的地方。所以我们如果每次工作后都调用sleep方法,那么这个isInterrupted检查就不是必须的了。
当在一个被阻塞的线程上调用interrupt方法时,阻塞调用就会被InterruptedException异常所终止。
注意:interrupted和isInterrupted的方法的区别?
interrupted是一个静态方法,它检查当前线程是否中断,调用这个方法会清除该线程的中断状态。
isInterrupted是一个实例方法,用来检查是否有线程已被中断,调用它不会改变中断状态的值。
4.线程状态
4个状态:看起来很难理解 新生 可运行 阻塞 死亡
5个状态:初始状态 可运行状态 运行状态 阻塞状态 死亡状态
为了便于理解将锁池状态和等待队列从阻塞状态中分离出来,分为7中状态:初始状态 可运行状态 运行状态 阻塞状态 死亡状态 锁池状态 等待队列 如下图
┌--------------------< 阻塞 死亡
↓ OS调度 ↑ ↑
初始-------> 可运行< ---------------> 运行 >-----------┤
t.start()启动 ↑ cpu到期或调用yield ↓ ↓o.wait()
└-----< 锁池 ←---------<┘←-------< 等待队列
获得锁标志 synchronized(o)
对七个状态进行解释:a.初始状态:创建线程,线程对象调用start方法。b.可运行:等待获得cpu资源 c.运行:获得cpu资源
d.阻塞:就是让出cpu资源,不是可运行态而是等待态 e.死亡:线程运行结束。
f.锁池:获得锁标志 g.等待队列:等待锁标志
何时会阻塞状态:a.等待数据输入(输入设备进行处理,而CPU不处理),则放入阻塞,直到输入完毕,阻塞结束后会进入可
运行状态。如果优先级比当前正在运行的线程优先级高就会抢占当前线程的cpu资源。
b.线程休眠,线程对象调用sleep()方法,阻塞结束后会进入可运行状态。如果优先级比当前正在运行的线
程优先级高就会抢占当前线程的cpu资源。
c.线程对象2调用线程对象1的join()方法,那么线程对象2进入阻塞状态,直到线程对象1中止。
5.线程属性
1)线程优先级:每个线程都有优先级,可以通过Thread.setPriority(int)设置线程的优先级
Thread类:
static int MIN_PRIORITY 线程的最低优先级 值我1
static int NORM_PRIORITY 线程的默认优先级 值为5
static int MAX_PRIORITY 线程的最高优先级 值为10
static void yield()导致当前执行线程进入让步状态,如果有优先级高于它的可运行线程,那么这些线程将会被调度
2)守护线程:主要是为其他线程提供服务,当只剩下守护线程,虚拟机就会退出。t.setDaemon(true)设置守护线程。
3)线程组:比如有大量的线程试图从服务器上下图片,此时想中断当前页面的载入,这个时候我们就要中断所有这些线程,如
果同时中断这些线程,我们就可以用线程组了。
ThreadGroup g=new ThreadGroup(groupName)//字符串groupName用来标识该组的
Thread t=new Thread(g,threadName)//创建线程并指定线程组
g.interrupt()//中断线程组中的所有线程。
4)未捕获异常处理器:线程的run方法不能抛出任何已检查的异常,但是未检查异常可以导致线程终止。然而并不需要任何
catch字句处理未检查异常,在死亡前,异常将被传递给未捕获异常的处理器处理。
为线程安装处理器:setUncaughtExceptionHandler()为任何线程安装处理器;
Thread.setDefaultUncaughtException()为所有线程安装一个默认的处理器。
6.线程同步
从jdk1.5开始,有两种机制来保护代码块不受并行访问的干扰。一个是通过synchronized关键字;一个是ReentrantLock类,
该类实现了Lock接口。
1)synchronized关键字:Java中每个对象都有个隐式的锁,一个方法用synchronized声明,那么对象的锁将保护这个方法
要调用这个方法,线程必须先获得对象的锁。
public synchronized void method(){........}//同步方法
public void method(){
synchronized(obj){......}//同步代码块,obj是我们在这个类中定义的锁对象。
}
Object类: notifyAll()//解除在该对象上调用wait的线程的阻塞状态,这个方法这能在同步方法或同步快用。
notify()//随机选择一个在该对象上调用wait的线程,解除它的阻塞状态。
wait()//导致线程进入等待状态直到被通知
2)ReentrantLock类实现同步:
public void transfer(int from,int to,int amount){
rlock.lock();
try{......}
finally{rlock.unlock();}
}
private Lock rlock=new ReentrantLock();
java.util.concurrent.locks.Lock接口:void lock()//获得这个锁,若这个锁被另一个线程持有,就发生阻塞。
void unlock()//释放这个锁
java.til.concurrent.locks.ReentrantLock类:ReentrantLock()//构建一个可被用来保护临界区的可重入锁。
3)条件对象:它是来管理那些已获得了锁却不能开始执行有用的工作的线程。
Condition sufficientFunds=rlock.newCondition();
public void transfer(int from,int to,int amount){
rlock.lock();
try{ while(...){ sufficientFunds.await();....... }
sufficientFunds.signalAll();
}
finally{rlock.unlock();}
}
private Lock rlock=new ReentrantLock();
Lock接口: Condition newCondition()//返回与该锁相关的一个条件对象。
Condition接口:void await()//把该线程放到条件的等待集中。
void signalAll()//解除该条件的等待集中所有线程的阻塞状态。
void signal()//在该条件的等待集中随机选择一个线程,解除其阻塞状态。
4)volatile关键字:为对一个实例的域的同步访问提供了一个免锁机制,如果我们把域声明为volatile,那么编译器和虚拟机就知道该域可能会被另一个线程并发更新。
5)锁测试和超时:我们一般都是调用lock方法来获得一个锁,但是有时候会发生阻塞,所以我们可以通过调用trylock方法试图来获得一个锁,入股成功就返回
true,否则false,并且线程可以继续做其他的事。
if(mylock.trylock()){
try{......}finally{mylock.unlock();}......
}else{.....}
我们也可以给trylock方法带一个超时参数:trylock(100,TimeUnit.MILLTSECONDS)
6)读写锁:ReentrantReadWrite rwl=new ReentrantReadWrite();
读锁:Lock readlock=rwl.readLock();
写锁:Lock writelock=rwl.writeLock();
8.执行器和同步器
我们往往会创建一些生存期限短的线程,此时我们就应该使用线程池,而执行器Executor类可以来构建线程池。
Executors类的工厂方法:newCachedThreadPool():构建一个线程池,对于每个任务,若有空闲线程可用,立即让他执行,否则创建一个新线程
newFixedThreadPool():构建一个大小固定的线程池,若提交的任务数量发育空闲线程数,那么得不到服务的任务将被置于队列中,当其
他任务执行完后他们就能运行了
newSingleThreadExecutor():是一个退化了的大小为1的线程池。由一个线程执行所有任务,一个接一个。
/*这三个方法返回一个实现了ExecutorService接口的ThreadPoolExecutor类的对象*/
使用连接池时应该做的事:调用Executors类中静态方法,调用submit来提交一个Runnable过Callable对象,若希望能够取消任务或若提交了一个Callable对
象,那就保存好返回的Future对象。当不想再提交任何任务时调用shutdown。
newScheduledThreadPool():
newSingleThreadScheduledExecutor():
结语:执行器、同步器看了一下没有看明白,想了想决定先放下,以后遇到的时候再看。