首页 > 安全资讯 >

Java基础知识-线程一(九)

17-03-22

Java基础知识-线程一(九),1 每个运行中的程序就对应一个进程,进程是处于运行过程中的程序,并且具有一定的独立功能。当一个程序运行时,内部可能包含多个顺序执行流,每个顺序执行流就是一个线程。

线程和进程的概念

1.每个运行中的程序就对应一个进程,进程是处于运行过程中的程序,并且具有一定的独立功能。当一个程序运行时,内部可能包含多个顺序执行流,每个顺序执行流就是一个线程。

2.线程是进程的组成部分,一个进程可以拥有多个线程,一个线程必须有一个父进程。

3.进程之间拥有独立的内存资源,而线程之间则是共享父进程里的全部资源。

4.线程是独立运行的,它并不知道进程中是否还有其他线程存在。线程是抢占式的运行,也就是我们所说的并发性。

线程的创建和启动

1.继承Thread类创建线程

public class FirstThread extends Thread{

//重写run方法,run方法的方法体就是线程执行体

public void run(){

//线程体...

}

}

//启动线程

FirstThread ft = new FirstThread();

ft.strat();//调用strat方法启动一个线程

2.实现Runnable接口创建线程

public class FirstRunnable implements Runnable{

//重写run方法

public void run(){

//线程体...

}

}

//启动线程

FirstRunnable fr = new FirstRunnable();

Thread thread = new Thread(fr);

thread.strat();//开启线程,开始执行子线程中的线程体

两种创建线程的比较:

使用继承Thread类的方式实现线程,编写比较简单,而且可以通过this直接访问当前线程。可是因为Java点继承的特点,将导致该类无法再继承其他类。

使用实现Runnable接口的方式实现线程,代码稍微繁琐,访问当前线程必须通过Thread.currentThread()方法,而且可以多个线程共享一个Runnable对象,非常适合多个相同线程来处理同一份资源的情况,而且可以继承其他的类。

因此大多数的情况下,我们都推荐使用Runnable接口的方式来创建线程

线程的一些基本方法

void start(); 使线程进入“就绪”状态

static Thread currentThread(); 总是返回当前正在执行的线程对象的引用

boolean isAlive(); 判断线程是否处于活动状态

String getName(); 返回该线程的名称

void setName(String name); 设置线程的名称

线程的生命周期

1.新建(New):

使用new关键字创建一个线程对象之后,该线程就处于了新建状态,这时它和其他Java对象一样,仅仅由Java虚拟机分配了内存,并初始化其成员变量的值。这个时候线程对象还没有表现出线程的动态特征,线程体也不会被执行

2.就绪(Runnable)

当线程对象调用start()方法以后,线程进入就绪状态,线程的run方法体并不会立刻执行,只是告诉程序该线程可以运行了。至于何时运行,则取决于JVM的线程调度器的调度。

3.运行(Running)

如果一个处于就绪状态的线程获得了CPU,则该线程会开始执行run()方法内的线程执行体,此时线程进入了运行状态。通常来讲一个线程不会一直处于运行状态(除非线程的线程体足够短,立刻就执行完成了),线程在运行过程中会被中断,目的是使其他线程获得执行的机会,线程调度取决于底层平台所采用的策略。

4.阻塞(Blocked)

当一个运行状态下线程除了被强制中断以外,还有可能被阻塞,从而进入到阻塞状态,当前执行的线程进入阻塞状态后,其他线程就可以获得执行的机会。被阻塞的线程在合适的时候会解除阻塞,从而再次进入就绪状态(不是运行状态),等待线程调度器再次调度它

可能引起线程阻塞的情况:

a.线程调用sleep()方法主动放弃所占用的处理器资源

b.线程调用一个阻塞式的IO方法,在该方法返回之前,线程被阻塞

c.线程在等待某个通知

线程的解除阻塞:

a.调用sleep()方法的线程经过了指定的时间

b.线程调用的阻塞式IO方法已经返回

c.线程正在等待某个通知时,其他线程发出了一个通知

5.死亡(Dead)

线程结束后,就会进入死亡状态。

如何结束线程?

a.当线程的线程体执行完成,线程正常结束

b.线程抛出一个未捕获的Exception或Error

c.直接调用线程stop()方法来结束该线程,此方法已经过时,并且容易导致死锁,所以不推荐使用

这里写图片描述

注意:

1.可以通过isAlive()方法来判断当前线程的状态,如果线程处于就绪、运行、阻塞状态,该方法返回true;

如果处于新建、死亡状态,该方法返回false;

2.只能对新建状态的线程调用start()方法,只能调用一次

线程的控制

1.join方法

join线程是一个线程等待另一个线程完成的方法。当某个线程A调用了其他线程B的join()方法时,那么线程A将被阻塞,直到线程B执行完毕后,线程A才重新进入就绪状态

重载方法:

join(long millis); 等待被join的线程时间最长为millis毫秒,如果在millis毫秒内被join的线程还没有执行结束,则不再等待

2.sleep方法

Thread类有一个静态方法sleep()方法,该方法可以使当前正在执行的线程暂定一段时间,进入阻塞状态。sleep()方法有一个参数,用来设置线程暂停多少毫秒,经过指定时间后,线程重新进入就绪状态

处于sleep()休眠中的线程不会被执行,即便当前没有任何线程在执行,该线程也不会执行

3.yield方法

Thread类提供了一个静态方法yield(),该方法可以让当前正在执行的线程暂停,但是不会阻塞该线程,而是直接将该线程转入到就绪状态。

注意:

a.完全有可能一个线程调用了yield方法后,线程调度器又调用该线程重新执行

b.当某个线程调用了yield()方法暂停后,只有优先级大于等于该线程的,并且处于就绪状态的线程才会获得执行的机会

线程的优先级

每个线程执行时都具有一定的优先级,优先级高的线程可能会比优先级低的线程获得更多的执行机会。

Thread类提供了setPriority(int newPriority);方法来设置线程的优先级,范围是1-10,数字越大优先级越高。

另外Thread还提供了三个常量来表示优先级:

MAX_PRIORITY:其值是10。

NORM_PRIORITY:其值是5。

MIN_PRIORITY:其值是1。

注意:

a.每个线程的优先级默认都和创建它的父线程优先级相同

b.我们通常都应该使用Thread提供的三个常量来设置优先级,这样程序才会有更好的移植性

线程的同步

在实际编程过程中,可能需要多个线程访问同一份资源的情况。但是由于调度器的随机性,可能会出现一些错误情况,这就是我们所说的线程安全问题。为了解决线程的安全问题,就需要线程的同步。

1.同步代码块

语法:

synchronized (obj) {

//需要同步的代码

}

同步代码块的作用

上面语法中 synchronized是一个同步关键字,后面括号中的obj就是同步监视器,任何一条线程开始执行同步代码块之前,必须先获得对同步监视器的锁定,当同步代码块执行完成后,该线程会释放对同步监视器的锁定,这样其他线程就能访问同步代码块中的内容了。总之任何时刻都只会有一个线程会获得对同步监视器的锁定。

注意:

Java中允许使用任何对象作为同步监视器,但是通过我们应该使用可能并发访问的共享资源充当同步监视器。

同步方法

就是用synchronized关键字修饰的方法,称为同步方法。同步方法无需指定同步监视器,同步监视器默认为this,也就是对象本身。

这里写图片描述

死锁

当两个线程相互等待对方释放同步监视器时就会发生死锁,而且Java虚拟机并没有检测死锁的机制,也不会采取任何措施来处理死锁,所以多线程编程时应该避免死锁的出现,一旦发生死锁,程序不会有任何异常,也不会给任何提示,只是两个线程处于阻塞状态,无法继续。

线程的并发协作

在java系统中,线程的调度有一定的随机性,通常无法准确的控制线程的轮换执行,但是我们可以通过一些机制来保证线程的协调运作,这就是线程的并发协作。

1.线程通信

a.wait()方法

该方法导致当前线程等待,直到其他线程调用该同步锁对象的notify()方法或者notifyAll()方法来唤醒该线程。

b.notify()方法

唤醒该同步锁对象上等待的某个线程,如果有多个线程在此同步锁对象上等待,则随机唤醒其中一个。

c.notifyAll()方法

唤醒在此同步锁对象上等待的所有线程

注意: wait、notify、notifyAll三个方法必须被在同步代码中被调用,并且只能由同步锁对象来调用

线程池

系统启动一个新线程的成本是比较高的,因为它涉及与操作系统的交互。在这种情形下,使用线程池可以很好的提高性能,尤其是当程序中需要创建大量生存周期很短的线程时,更应该考虑使用线程池。

线程池工作机制:

线程池在系统启动是即创建大量的空闲的线程,

程序将一个Runnable对象传给线程池,线程池就会调用一个线程来执行它们的run方法,当run方法执行完成后,该线程也不会死亡,而是再次返回线程池中称为空闲线程,等待执行下一个Runnabe对象中的run方法

Java提供的线程池

1、调用Executors类的静态工厂方法创建一个ExecutorService对象,该对象代表一个线程池

2、创建Runnable实现类作为线程执行对象

3、调用ExecutorService对象的submit()方法来提交Runnable实现类对象,交给一个线程池中线程执行

4、在需要的时候调用shutdown()方法关闭线程池

ThreadLocal类

1.ThreadLocal类的作用

ThreadLocal代表了一个线程局部变量,通过把数据放在ThreadLocal中就可以让每个线程持有一个独立的变量副本,从而避免并发访问的线程安全问题

2.ThreadLocal类的常用方法

a.T get(); 返回此线程中的变量副本值

b.void remove(); 删除此线程局部变量中当线程的值

c.void set(T t); 设置此线程局部变量中当前线程副本中的值

3.什么时候需要使用ThreadLocal

如果多个线程之间需要共享资源,以达到线程之间的通信功能,就使用同步机制;如果需要隔离多个线程之间的共享冲突,则可以使ThreadLocal

相关文章
最新文章
热点推荐