------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
线程概述
在生活中,我们经常在同一时间完成很多事情。例如人可以同时进行呼吸、思考问题、走路等活动;还有计算机用户可以进行一边听音乐,一边上网等同时进行的活动。这种思想就是Java中的并发,并发完成的每一件事情叫做进程。
进程和线程
进程就是一个正在运行的有自身执行地址的程序。在多任务操作系统中,分配CPU时间给每一个进程。CPU在指定时间片段内执行某个进程,然后在下一个时间片段跳到另外一个进程中执行,由于转换时间很快,所有看起来像同时进行的。
线程是进程内部的执行的任务,是进程中的实体。咱们通常说的多线程就是指一个进程可以同时运行多个任务,每个任务由一个线程完成。即,多个线程可以同时运行,并且在一个进程内执行不同的任务。
实现线程的两种方式
继承Thread类
实例化Thread类后的对象就是线程,我们启动一个线程需要建立Thread实例。Thread(String threadName)和Thread()分别是Thread类中常用的两个构造方法,其中第一个构造方法是创建一个名为threadName的线程对象。继承Thread类创建一个新的线程的语法如下:
public static void main(String[] args) {
//第一种创建线程的方法,start启动线程
new ThreadTest().start();
//第二种创建线程的方法
ThreadTest tt = new ThreadTest();
new Thread(tt).start();
}
}
class ThreadTest extends Thread{
//run(),定义run方法里要执行的代码,线程功能
public void run(){
//...
}
}
实现Runnable接口
除了继承Thread类来创建线程,我们还可以通过实现Runnable接口来创建线程,这样就可以让继承其他的类,就避免了单继承的局限性。所以在定义线程时建议使用实现Runnable的方式。以下是一个实现Runnable接口的子类的小例子:
class Ticket implements Runnable //extends Thread
{
//定义100张票
private int tick = 50;
public void run(){
while(true){
//当没有余票时,结束
if(tick>0){
System.out.println(Thread.currentThread()+"...sale:"+tick--);
}
}
}
}
//创建一个实现了runnable接口的子类的对象
Ticket t= new Ticket();
//创建两个线程对象(2个售票员),与t关联起来
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
//启动线程
t1.start();
t2.start();
线程的生命周期
线程的生命周期分为7个状态:出生、就绪、执行、等待、休眠、阻塞和死亡。出生就是用户创建线程的状态,用户调用start方法之前都是出生状态,调用后进入就绪状态,得到资源后进入运行状态。一旦线程进入可执行状态,线程就在就绪与运行状态下交替,同时也有可能进入等待、休眠、阻塞和死亡状态。下图描述了线程生命周期的各种状态:
操作线程的方法
操作线程的方法就是可以使线程从某一种状态过渡到另一种状态的方法,分别如下:
static void sleep
(long millis):线程休眠,在指定的毫秒数内让当前正在执行的线程休眠。
void join():线程挂起,指暂停当前正在执行的线程,但另一个线程执行完毕后,才继续执行当前线程。该方法通常放在另一个线程的run方法内。
void stop():线程停止,使用该方法停止线程,目前不提倡使用该方法停止线程,而是提倡在run方法中使用无限循环的方法,然后使用一个布尔型标记控制循环的停止。
void interrupt():线程中断,当由于使用sleep或wait方法使线程进入就绪状态时,可以使用Thread类中的interrupt方法使线程离开run方法,同时结束线程。
static void yield():线程礼让,暂停当前正在执行的线程对象,并执行其他线程。给当前正处于运行状态下的线程一个提醒,可以将资源让给其他线程。但不保证当前线程会资源礼让。
线程的同步技术
在多线程中需要使用到线程同步机制来访者资源访问的冲突。实际问题有多窗口买票的问题,以下是实例代码:
class NewThread2 implements Runnable{
private int ticket = 100;
Object ob = new Object();//定义锁,同步的线程必须共用一把锁
public void run() {
// TODO Auto-generated method stub
while(true){
synchronized(ob) //线程同步,需要被同步的代码都放在同步块中
{
if(ticket>0){
try {
Thread.sleep(5);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread()+"...sale:"+ticket--);
}
}
}
}
}
线程间的通信
线程之间的通信使用wait、notify和notifyAll等方法来实现。与sleep(不释放锁)方法让线程睡眠的效果相同的方法是wait(time)(释放锁)方法。还有一种无参的wait方法,使用后线程进入无休止的等待中,需要使用notify或notifyAll方法来唤醒。并且wait、notify和notifyAll方法只能在同步块或同步方法中使用。
总结
除了以上这些,线程中还有一个线程优先级的概念。线程的优先级可以表明线程的重要性,同一状态下,优先级高的线程得到时间片资源的概率就高,进入运行状态的机会大。有几个成员变量代替了优先级的高低:最高MAX_PRIORITY(10)、最低
MIN_PRIORITY(1)和正常
NORM_PRIORITY(5)。