几个概念
进程:程序的一次运行(从程序开启->程序结束)
线程:操作系统调度的基本单位
线程不可独立,必须依附在进程之下
并行:两个或多个事件在同一时刻发生
并发:两个或多个事件在同一段时间内发生
进程是程序运行的实例
进程是OS分配系统资源的单元
多线程程序:多个线程并发执行的程序
线程的创建方式
线程的四种创建方式:
继承Thread类
实现Runnable接口
实现Callable接口
线程池创建
继承Thread类,重写run方法
class MyThread extends Thread{
@override
public void run(){
// ....
}
}
Thread t=new MyThread("myThread");
t.start();
创建一个打印线程
继承Thread类开启线程的缺点:Java只运行单继承,但一个继承Thread之后便无法再有其他父类的,这是不好的。
实现Runnable接口
class MyRunnable implements Runnable{
public void run(){
//...
}
}
Thread t=new Thread(new MyRunnable(),"myRunnable");
t.start();
实现Callable接口
Future接口方法:isDone;cancel;get
class MyCallable implements Callable<String>{
public String call(){
// ...
}
}
//call方法是有返回结果的
线程池创建 Executor
ExecutorService e = Executers.newFixedThreadPo ol(10);
e.execute(new Runnable());
实现接口和继承Thread开启线程的比较
接口更适合多个相同的程序代码的线程去共享一个资源
接口可以避免java中的单机成的局限性
接口代码可以被多个线程共享,代码和线程独立
线程池只能放入Runnable或Callable接口的线程
Runnable和Callable接口比较
Callable接口的线程能返回执行结果
Callable接口的call方法运行抛出异常
实现Callable接口的线程可以调用Future.cancle取消执行
(Callable接口支持返回执行接口,此时调用FutureTast.get获取到线程执行的结果,FutureTast.get会阻塞主调这,直至得到线程的执行结果)
多线程控制类
为了保证多线程的三个特性,Java引入了很多线程控制机制,如:
ThreadLocal:线程本地变量
yuanzi lei:保证变量的氧原子操作
Lock类:保证线程有序性
Volatile:保证线程变量可见性
ThreadLocal
???不是很明白
ThreadLocal提供线程局部变量,即为使用线条变量的每一个线程维护一个该变量的副本。当某些数据时一个数据为作用域并且并且不同线程为不
多线程特性
原子性
院子性,一个操作或多个操作要么全部执行,并且执行过程背会被任何因素打断,要么不执行
可见性
可见性是指多个线程访问一个变量,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值
(对于单线程而言,可见性问题是不存在的)
有序性
有序性即一些代码的执行顺序应当是被有先后的
线程安全问题
线程安全概念
???
多个线程对共享数据有写操作时,就有可能会发生线程安全问题
线程同步
七种线程同步机制
同步代码块
同步方法
同步锁(reenreantlock)
特殊域变量(volatile)
局部变量(ThreadLocal)
阻塞队列(linkedBlokingQueue)
原子变量(atomic)
同步方法的同步锁
对于非static方法,同步锁为this
对于static方法,同步锁为.class字节码对象
同步锁
lock.lock();
try{
//...
}
finally{
lock.unlock();
}
一旦lock开启之后,必须unlock否则会死锁
sychronized和Lock区别
sychronized是关键字,在jvm层面,Lock是个Java类
synchronized无法判断是否获取锁的状态
synchronized会自动释放锁,Lock需要手动释放锁
sychronized受关联的两线程,如果一个线程持有锁,另一个线程会一直等待锁的释放;Lock则如果尝试获取不到锁,线程可以不用一直等待就就是了
synchronized锁可冲入,不可中断,非公平,Lock锁可冲入,可中断,可公平
lock锁适合大量同步代码的同步问题,synchronized锁适合代码少量的同步问题
线程生命周期
新建NEW
new关键字创建一个线程之后,该线程就处于新建状态
JVM为线程分配内存,初始化成员变量值
就绪
当线程对象调用start方法后,该线程处于就像状态
JVM为线程创建方法栈和程序计数器,等待线程调度器调用
运行
就绪状态的线程获得CPU资源,开始运行run方法
阻塞
当发生如下情况,线程会进入use状态
线程调用sleep方法主动放弃所占用的处理器资源
线程调用一个阻塞式IO方法,在该方法返回时,该线程处于阻塞状态
线程试图获得一个同步锁(同步监视器),但该同步监视器正在被其他线程所持有
线程在等待某个通知(notify)
程序调用线程的suspend方法将线程挂起(这个方法容易导致死锁,应当比避免使用)
死亡
线程会以如下三种方式结束,结束后处于死亡状态
run或call方法执行完成,线程正常结束
线程抛出一个未捕获的Exception和Error
线程调用stop结束线程(该方法容易导致死锁,应避免使用)
线程死锁
死锁:多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些线程都将无法向前推进
死锁的产生
互斥条件
不可剥夺条件
请求与保持条件
循环等待条件
产生死锁的代码
死锁处理
预防死锁
避免死锁
检测死锁
解除死锁
线程通信
线程间常用的通信方式如下:
休眠唤醒方式
Object的wait、notifuy、notifyAll
CountDownLatch用于某个线程等待其他线程执行完后执行
CyclicBarrier一组线程等待至某个状态之后再全部同时执行
Semaphore:用于控制对某组资源的访问权限
休眠唤醒方式
多线程打印奇偶数
notyfy() wait() 必须放在sychronized代码块中
CountDownLatch
等待多个线程执行完毕后在执行
9:14
近期评论