cmd看字节码命令 javap -verbose xx.class
start-----readtorun-----run-------stop
wait|sleep
|
bolcked
创建线程的多种方式:
1.继承Thread类
extends Thread
重写run方法
@Override
public void run(){
//getName()可以获取线程名称
}
jkd6后使用interrupt()线程中断,判断使用interrupted()
stop并未真正中断,线程锁以及资源并未真正释放
setDaemon(true);守护线程
作用:当主线程结束,该线程任务没有执行完毕也会随主线程一样结束
拓展:
ThreadGroup线程组
可以创建一个线程组,然后把线程加到线程组中
树状结构
2.实现Runnable接口
优点:面向接口编程
implements Runnable
和继承Thread类一样它也要实现run接口
作为线程任务存在
3.匿名内部类的方式
//第一种:
new Thread(){
public void run(){
System.out.println("Thread");
}
}.start();
//第二种:
new Thread(new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("Runnable");
}
}).start();
拓展:
new Thread(new Runnable(){
@Override
public void run() {
System.out.println("Runnable");
}
}){
public void run(){
System.out.println("Thread");
}
}.start();
new Thread的时候,会把()里面Runable带到Thread类中的构造方法,
经过init初始化等一系列的赋值,Thread类中属性target会得到赋值,
然后执行start,会去找Thread(父)类中的Run方法,然而
{}之间的是子类,按照java基础语法多态可知,此时父类中的Run方法并没什么卵用,target只是陪玩,
会按照子类中的Run方法来执行
根据java语法多态,先执
4.带返回值的线程
详解:
http://blog.youkuaiyun.com/javazejian/article/details/50896505
实现callable方法
implements Callable<T>
@Override
public T call() throws Exception{}
结合FutureTask该类实现了Runnable接口
5.定时器
Timer timer = new Timer();
timer.schedule();/指定日期、指定延时、指定第一次日期间此后间隔执行
延时多长时间此后间隔执行
TimerTask类实现了Runable方法
缺点:不可控,当任务没有执行完毕或者要提交不同任务时,
quartz框架可以实现
6.线程池的实现
Executor threadPool = Executors.newFixedThreadPool(10);
newFixedThreadPool(10)创建固定数量线程
threadPool.execute(new Runnable(){
public void run(){
}
})
ExecutorService threadPool =Excutors.newCachedThreadPool()根据线程任务不够用就创建,够用就回收
threadPool.shutdown();停掉线程池
7.Lambda表达式实现
函数式编程
优点:代码简洁、实现方便、对并发支持性好性能高
8.Spring实现多线程
jdk8新特性
使用Maven构建项目,pom.xml配置jdk以及spring
@Configuration
@ComponentScan("") 扫描包
@EnableAsync
构建Config
@Service注释类
@Async 注释方法
构建方法
线程带来的风险:
1.线程安全性问题
多线程环境下
多个线程共享一个资源
对资源进行非原子性操作
什么时候出现:多线程环境下;多个线程共享一个资源;对资源进行读写操作
解决:方法加关键字synchronized
Synchronized的原理与使用
内置锁
每一个java中的对象都是一个内置锁,java在进入同步代码块之前都必须先获得锁,
也就是获得内置锁,获得之后就可以执行同步代码块,那其它线程就
无法获得了,只有等它释放了锁其他线程才能进来,就相当于被synchronzed
修饰的方法体相当于一个原子性操作,这样就保证了线程安全性。
互斥锁(一个线程进来,那么另一个线程不能进来)
Synchronized使用
修饰普通方法(放在普通方法上,那么内置锁就是当前类的实例)
修饰静态(static)方法(放在普通方法上,那么内置锁就是当前的class字节码对象)
修饰代码块 synchronized(Object){}
Synchronized保证线程安全的原理(jvm层面)
通过monitorenter进入和monitorexit退出
无论什么情况,这个锁都会释放
任何对象都可以作为锁,锁信息存在于对象头中
对象头中的信息
1Mark Word 对象的哈希值和锁信息
2Class Metadata Address 类型地址
3Array Length 数组对象多了一个数组长度
jdk1.6后引入了偏向锁、轻量级锁、重量级锁
偏向锁
出现原因:
每次获取锁和释放锁会浪费资源
很多情况下,竞争锁不是由多个线程,而是由一个线程在使用。
偏向锁的markword会记录:线程id、Epoch、对象的分带
年龄信息、是否是偏向锁、锁标志位
适用场景:
只有一个线程在访问同步代码块的场景
很多情况下不会提高性能
轻量级锁
自旋:自己不停的旋转等待去执行,旋的是cpu的时间片,也就是空转cpu,
一直等待被唤醒之后
多个线程可以同时
锁重入
syncronized和lock都是重入锁
Demo:
public synchronized void a(){
System.out.println("a");
b();
}
public synchronized void b(){
System.out.println("b");
}
上例中b()是可以执行的,这就是锁的重入
当是同一个对象的两个锁,是可以锁住的
当锁对应的是不同的对象,那么就不能锁住
Thread.activerCount();线程活跃数
2.活跃性问题
死锁:都有对方的资源但是都不释放,导致进程无法进行
产生原因:当一个线程永远都只有一把锁,其它线程尝试获得该把锁
的时候,也就发生了死锁,无非就是争夺资源
cmd输入命令jconle可以检测死锁
jvm对死锁处理能力较弱,第一死锁不会每次都发生,第二发生后在jvm中
线程就死掉了,jvm不会对其释放掉,而数据库这方面处理的很好
demo:
private Object obj1 = new Object();
private Object obj2 = new Object();
public void a(){
synchronized(obj2){
synchronized(obj1){
System.out.println("a");
}
}
}
public void b(){
synchronized(obj1){
synchronized(obj2){
System.out.println("b");
}
}
}
饥饿:线程有优先级,优先级低的可能一直得不到CPU
高优先级吞噬所有低优先级的CPU时间片
setPriority() 设置优先级
线程被永久堵塞在一个等待进入同步块的状态
等待的线程永远不被唤醒
如何尽量避免饥饿问题
设置合理的优先级
使用锁来代替synchronized
活锁:类似平时两人相对而走,循环同时避让
3.性能问题
cpu会为各个线程分配时间片,
当这个任务在这个时间片中执行完了之后,
cpu会执行下一个,因为时间片很短,所以我们看起来这么多任务
是一块执行的,也就是转的非常快,转的过程就是上下文切换,
cpu通过一定的算法循环执行这些任务,但是切换的时候
要保存当前任务的执行状态,以便下一次执行能够加载出来
继续执行,所以在转换的过程中上下文切换是蛮消耗资源的
start-----readtorun-----run-------stop
wait|sleep
|
bolcked
创建线程的多种方式:
1.继承Thread类
extends Thread
重写run方法
@Override
public void run(){
//getName()可以获取线程名称
}
jkd6后使用interrupt()线程中断,判断使用interrupted()
stop并未真正中断,线程锁以及资源并未真正释放
setDaemon(true);守护线程
作用:当主线程结束,该线程任务没有执行完毕也会随主线程一样结束
拓展:
ThreadGroup线程组
可以创建一个线程组,然后把线程加到线程组中
树状结构
2.实现Runnable接口
优点:面向接口编程
implements Runnable
和继承Thread类一样它也要实现run接口
作为线程任务存在
3.匿名内部类的方式
//第一种:
new Thread(){
public void run(){
System.out.println("Thread");
}
}.start();
//第二种:
new Thread(new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("Runnable");
}
}).start();
拓展:
new Thread(new Runnable(){
@Override
public void run() {
System.out.println("Runnable");
}
}){
public void run(){
System.out.println("Thread");
}
}.start();
new Thread的时候,会把()里面Runable带到Thread类中的构造方法,
经过init初始化等一系列的赋值,Thread类中属性target会得到赋值,
然后执行start,会去找Thread(父)类中的Run方法,然而
{}之间的是子类,按照java基础语法多态可知,此时父类中的Run方法并没什么卵用,target只是陪玩,
会按照子类中的Run方法来执行
根据java语法多态,先执
4.带返回值的线程
详解:
http://blog.youkuaiyun.com/javazejian/article/details/50896505
实现callable方法
implements Callable<T>
@Override
public T call() throws Exception{}
结合FutureTask该类实现了Runnable接口
5.定时器
Timer timer = new Timer();
timer.schedule();/指定日期、指定延时、指定第一次日期间此后间隔执行
延时多长时间此后间隔执行
TimerTask类实现了Runable方法
缺点:不可控,当任务没有执行完毕或者要提交不同任务时,
quartz框架可以实现
6.线程池的实现
Executor threadPool = Executors.newFixedThreadPool(10);
newFixedThreadPool(10)创建固定数量线程
threadPool.execute(new Runnable(){
public void run(){
}
})
ExecutorService threadPool =Excutors.newCachedThreadPool()根据线程任务不够用就创建,够用就回收
threadPool.shutdown();停掉线程池
7.Lambda表达式实现
函数式编程
优点:代码简洁、实现方便、对并发支持性好性能高
8.Spring实现多线程
jdk8新特性
使用Maven构建项目,pom.xml配置jdk以及spring
@Configuration
@ComponentScan("") 扫描包
@EnableAsync
构建Config
@Service注释类
@Async 注释方法
构建方法
线程带来的风险:
1.线程安全性问题
多线程环境下
多个线程共享一个资源
对资源进行非原子性操作
什么时候出现:多线程环境下;多个线程共享一个资源;对资源进行读写操作
解决:方法加关键字synchronized
Synchronized的原理与使用
内置锁
每一个java中的对象都是一个内置锁,java在进入同步代码块之前都必须先获得锁,
也就是获得内置锁,获得之后就可以执行同步代码块,那其它线程就
无法获得了,只有等它释放了锁其他线程才能进来,就相当于被synchronzed
修饰的方法体相当于一个原子性操作,这样就保证了线程安全性。
互斥锁(一个线程进来,那么另一个线程不能进来)
Synchronized使用
修饰普通方法(放在普通方法上,那么内置锁就是当前类的实例)
修饰静态(static)方法(放在普通方法上,那么内置锁就是当前的class字节码对象)
修饰代码块 synchronized(Object){}
Synchronized保证线程安全的原理(jvm层面)
通过monitorenter进入和monitorexit退出
无论什么情况,这个锁都会释放
任何对象都可以作为锁,锁信息存在于对象头中
对象头中的信息
1Mark Word 对象的哈希值和锁信息
2Class Metadata Address 类型地址
3Array Length 数组对象多了一个数组长度
jdk1.6后引入了偏向锁、轻量级锁、重量级锁
偏向锁
出现原因:
每次获取锁和释放锁会浪费资源
很多情况下,竞争锁不是由多个线程,而是由一个线程在使用。
偏向锁的markword会记录:线程id、Epoch、对象的分带
年龄信息、是否是偏向锁、锁标志位
适用场景:
只有一个线程在访问同步代码块的场景
很多情况下不会提高性能
轻量级锁
自旋:自己不停的旋转等待去执行,旋的是cpu的时间片,也就是空转cpu,
一直等待被唤醒之后
多个线程可以同时
锁重入
syncronized和lock都是重入锁
Demo:
public synchronized void a(){
System.out.println("a");
b();
}
public synchronized void b(){
System.out.println("b");
}
上例中b()是可以执行的,这就是锁的重入
当是同一个对象的两个锁,是可以锁住的
当锁对应的是不同的对象,那么就不能锁住
Thread.activerCount();线程活跃数
2.活跃性问题
死锁:都有对方的资源但是都不释放,导致进程无法进行
产生原因:当一个线程永远都只有一把锁,其它线程尝试获得该把锁
的时候,也就发生了死锁,无非就是争夺资源
cmd输入命令jconle可以检测死锁
jvm对死锁处理能力较弱,第一死锁不会每次都发生,第二发生后在jvm中
线程就死掉了,jvm不会对其释放掉,而数据库这方面处理的很好
demo:
private Object obj1 = new Object();
private Object obj2 = new Object();
public void a(){
synchronized(obj2){
synchronized(obj1){
System.out.println("a");
}
}
}
public void b(){
synchronized(obj1){
synchronized(obj2){
System.out.println("b");
}
}
}
饥饿:线程有优先级,优先级低的可能一直得不到CPU
高优先级吞噬所有低优先级的CPU时间片
setPriority() 设置优先级
线程被永久堵塞在一个等待进入同步块的状态
等待的线程永远不被唤醒
如何尽量避免饥饿问题
设置合理的优先级
使用锁来代替synchronized
活锁:类似平时两人相对而走,循环同时避让
3.性能问题
cpu会为各个线程分配时间片,
当这个任务在这个时间片中执行完了之后,
cpu会执行下一个,因为时间片很短,所以我们看起来这么多任务
是一块执行的,也就是转的非常快,转的过程就是上下文切换,
cpu通过一定的算法循环执行这些任务,但是切换的时候
要保存当前任务的执行状态,以便下一次执行能够加载出来
继续执行,所以在转换的过程中上下文切换是蛮消耗资源的