您的位置:首页 > 其它

线程基础

2016-03-13 16:07 267 查看
1、线程状态:

新生状态

用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态,处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态(rumnable)

就绪状态

处于就绪状态的线程已经具备了运行的条件,但还没有分配到cpu,处于线程就绪队列,等待系统为其分配CPU,等待状态并不是执行状态,当系统选定一个等待执行的Thread对象后,他/她就会从等待执行状态进入执行状态,系统挑选的动作称之为“CPU调度”。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。

运行状态

在运行状态的线程执行自己的run方法中代码,知道调度其他方法而终止、或等待某资源而阻塞或完成任务而死亡。如果在给定的时间片内没有执行结束,就会被系统给换下来回到等待执行状态

阻塞状态

处于运行状态的线程在某些情况下,如执行了sheep(睡眠)方法,或等待I/O设备等资源,将让出CPU并暂时停止自己的运行,进入阻塞状态,在阻塞状态的线程不能进入就绪队列,只有当引起阻塞的原因消除时,如睡眠时间已到,或等待的I/O设备空闲下来,线程便转入就绪状态,重新到就绪队列中排队等待,被系统选中后从原来停止的位置开始继续运行。

死亡状态

死亡状态是线程生命周期中的最后一个阶段,线程死亡的原因有两个。一个是正常运行的线程完成了它全部工作,另一个是线程被强制性的终止,如通过执行stop或destroy方法来终止一个线程(不推荐使用这两个方法,前者会产生异常,后者是强制终止,不会释放锁)

2、停止线程

1)自然终止:线程体正常执行完毕

2)外部干涉

1、在线程类中定义线程体使用的标识

2、线程体使用该标识

3、提供对外的方法改变该标识

4、外部根据条件调用该方法即可

3、阻塞

1) join:合并线程

2)yield:暂停自己的线程 静态方法

3) sleep:休眠,不释放锁

*与时间相关:倒计时

*模拟网络延时

4、线程的同步(并发)

1)由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问。

2)由于我们可以通过private关键字来保证数据对象只能被方法访问,所有我们只需针对方法提出一套机制,这套机制就是synchronized关键字,它包括两种用法:synchronized方法和synchronized块。

同步:多个线程访问同一份资源 确保资源安全--->线程安全

同步块

synchronized(引用类型|this|类.class){

}

同步方法

public synchronized void test1(){

}

线程安全:保证资源安全,相对效率低

锁定范围过大:效率低下

锁定范围过小:没有保证资源安全

1

public class SynDemo1 {
public static void main(String[] args) {
JvmThread thread1 = new JvmThread(100);
JvmThread thread2 = new JvmThread(500);
thread1.start();
thread2.start();
}
}

class JvmThread extends Thread{
private long time;
public JvmThread() {
}
public JvmThread(long time){
this.time = time;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+
"-->创建:"+Jvm.getInstance1(time));
}
}

/**
* 单例设计模式
* 确保一个类只有一个对象
* 懒汉式(使用的创建对象):
*         1、构造器私有化,避免外部直接创建对象
*         2、声明一个私有的静态变量
*         3、对外提供一个公共的静态方法访问该变量,
*              如果变量没有对象,创建对象,否则直接返回
* @author qjc
*
* 2016-3-13
*/
class Jvm{
private static Jvm instance = null;
private Jvm() {
}
//线程不安全
public static Jvm getInstance1(long time){
if(null==instance){
try {
Thread.sleep(time);//延时 放大错误
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new Jvm();
}
return instance;
}
public static synchronized Jvm getInstance2(long time){
if(null==instance){  //安全
try {
Thread.sleep(time);//延时 放大错误
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new Jvm();
}
return instance;
}
public static Jvm getInstance3(long time){
//a b c d e都在此等待 -->效率不高 存在对象也需要等待
synchronized (Jvm.class) {  //静态代码块里没有this
if(null==instance){   //安全
try {
Thread.sleep(time);//延时 放大错误
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new Jvm();
}
return instance;
}
}
//double checking 双重检查
public static Jvm getInstance4(long time){
//b c d e 直接返回对象-->效率 提供已经存在对象的访问效率
if(null==instance){
//a 创建对象
synchronized (Jvm.class) {  //静态代码块里没有this
if(null==instance){  //安全
try {
Thread.sleep(time);//延时 放大错误
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new Jvm();
}
}
}
return instance;
}
}


5、死锁

建立在同步之上,过多的同步容易造成死锁

2

/**
* 过多同步容易造成死锁
* @author qjc
*
* 2016-3-13
*/
public class SynDemo2 {
public static void main(String[] args) {
Object goods = new Object();
Object money = new Object();
Test t1 = new Test(goods,money);
Test t2 = new Test(goods,money);
Thread proxy = new Thread(t1);
Thread proxy2 = new Thread(t2);
proxy.start();
proxy2.start();
}
}

class Test implements Runnable{
Object goods;
Object money;
public Test(Object goods, Object money) {
super();
this.goods = goods;
this.money = money;
}
@Override
public void run() {
while (true) {
test();
}
}
public void test() {
synchronized (goods) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (money) {
}
}
System.err.println("一手给钱");
}
}
class Test2 implements Runnable{
Object goods ;
Object money ;
public Test2(Object goods, Object money) {
super();
this.goods = goods;
this.money = money;
}
@Override
public void run() {
while (true) {
test();
}
}
public void test() {
synchronized (money) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (goods) {
}
}
System.err.println("一手给货");
}
}


解决死锁方法:生产者消费者模式(不是设计模式

生产者消费者问题,也称有限缓冲问题,是一个多线程同步问题的经典案例。该问题描述了两个共享固定大小缓冲区的线程----即所谓的“生产者”和“消费者” ---在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。在此同时,消费者也在缓冲区消费这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。

要解决该问题,就必须谈生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。通常常用的方法由信号灯法、管程等。如果解决方法不够完善,则容易出现死锁的情况。出现死锁时,两个线程都会陷入休眠,等待对方唤醒自己。

3

/**
* 一个场景,共同的资源
* 生产者消费者模式:信号灯法
*     wait():等待,释放锁
*      sleep():不释放锁
*      notify()/notifyAll():唤醒
* @author qjc
*
* 2016-3-13
*/
public class Movie {
private String pic;
/**
* 信号灯
*     flag = T 生产者生产,消费者等待,生产完成后通知消费
*     flag = F 消费者消费,生产者等待,消费完后通知生产
*/
private boolean flag = true;
public synchronized void play(String pic){
if(!flag){ //生产者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//开始生产
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("生产了"+pic);
//生产完毕
this.pic = pic;
//通知消费
this.notify();
//生产者停下
this.flag = false;
}
public synchronized void watch(){
if(flag){ //消费者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//开始消费
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(pic);
//消费完毕
System.out.println("消费了"+pic);
//通知生产
this.notifyAll();
//消费停止
this.flag = true;
}
}

/**
* 生产者
* @author qjc
*
* 2016-3-13
*/
public class Player implements Runnable{
private Movie m;
public Player(Movie m) {
super();
this.m = m;
}

@Override
public void run() {
for(int i=0;i<20;i++){
if(0==i%2){
m.play("左青龙");
}else{
m.play("右白虎");
}
}
}
}

/**
* 消费者
* @author qjc
*
* 2016-3-13
*/
public class Watcher implements Runnable {
private Movie m;
public Watcher(Movie m) {
super();
this.m = m;
}

@Override
public void run() {
for(int i=0;i<20;i++){
m.watch();
}
}
}

/**
* 测试
* @author qjc
*
* 2016-3-13
*/
public class App {
public static void main(String[] args) {
//共同资源
Movie m = new Movie();
//多线程
Player p = new Player(m);
Watcher w = new Watcher(m);
new Thread(p).start();
new Thread(w).start();
}
}


sleep()wait()区别

对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的。

sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。

在调用sleep()方法的过程中,线程不会释放对象锁。

而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备

获取对象锁进入运行状态。

6、任务调度

Timer 定时器类

TimerTask 任务类

通过java timer timertask :(spring的任务调度就是通过他们来实现的)

在这种实现方式中,Timer类实现的是类似闹钟的功能,也就是定时或者每个一定时间触发一次线程。其实,Timer类本身实现的就是一个线程,只是这个线程是用来实现调用其他线程的。而TimerTask类似一个抽象类,该类实现了Runnable接口,所以按照前面的介绍,该类具备多线程的能力。

在这种实现方式中,通过继承TimerTask是该类获得多线程的能力,将需要多线程执行的代码书写在run方法内部,然后通过Timer类启动线程的执行。

在实际使用时,一个Timer可以启动任意多个TimerTask实现线程,但是多个线程之间会存在阻塞。所以如果多个线程之间如果需要完全独立运行的话,最好还是一个Timer启动一个TimerTask实现。

4

/**
* @author qjc
*
* 2016-3-13
*/
public class TimeDemo {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("so easy..");
}
}, new Date(System.currentTimeMillis()+1000)
, 500);//参数3:每隔500秒调用一次,没有参数3只调用一次
}
}


第三方框架:quartz
juc

7、总结

1、创建线程:

继承Thread

实现Runnable

实现Callable

2、线程状态



1)新生-->start -->就绪-->运行-->阻塞-->终止

2)终止线程

3)阻塞:join yield
sleep



3、线程的信息

1)Thread.currentThread

2)
获取名称设置名称设置优先级判断状态

4、同步 ----对同一份资源
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: