线程

来源:互联网 发布:爱知科技工资怎么样 编辑:程序博客网 时间:2024/06/05 18:00

我们使用计算机可以同时完成听歌、打印、看电影等活动,这种思想放在java中被称为并发,而将并发完成的每一件事情称为线程


一、进程与线程

       进程是程序的一次动态执行过程,经历了从代码加载、执行到执行完毕的一个完整过程,这个过程也是进程本身从产生、发展到最终消亡的过程。

       进程线程一样都是实现并发的一个基本单位。线程是比进程更小的执行单位,线程是在进程的基础上进行的进一步的划分。所谓的多线程是指一个进程在执行过程中可以产生多个线程,这些线程可以同时存在、同时运行,一个进程可能包含了多个同时执行的线程。所有的线程一定要依附于进程才能够存在,进程一旦消失线程也一定会消失。


二、多线程的实现

java中要想实现多线程的程序,就必须依靠一个线程的主体类,该类可以继承继承java.lang.Thread和实现java.lang.Runnable接口。

实现线程的方式主要有两种:继承java.lang.Thread和实现java.lang.Runnable接口。

2.1继承Thread类实现多线程

java.lang.Thread是一个负责线程操作的类,任何的类只要继承Thread类就可以成为一个线程的主类,主类就有它的使用方法,而线程启动的主方法是需要覆写Thread类中的run()方法实现的。线程主体类的定义格式如下:

class 类名称 extends Thread(){   属性···   方法···public void run(){       线程主体方法;      }}

我们来看一个例子:

这里我写了两个类,一个是MyThread继承了Thread类,另一个是调用我们的线程的类。

package com.test2;public class MyThread extends Thread{private String title;public MyThread(String title){this.title=title;}public void run() {for(int x=0;x<5;x++){System.out.println(this.title+"运行,x="+x);}};}
线程类写好后我们看启动线程的类:

package com.test2;public class TestDemo {public static void main(String[] args) {MyThread mt1=new MyThread("线程A");MyThread mt2=new MyThread("线程B");MyThread mt3=new MyThread("线程C");mt1.start();mt2.start();mt3.start();}}
程序运行的每一次结果都是不一样的,但一定会是交替出现的才对。如:

线程A运行,x=0线程A运行,x=1线程B运行,x=0线程C运行,x=0线程C运行,x=1线程B运行,x=1线程A运行,x=2线程A运行,x=3线程A运行,x=4线程B运行,x=2线程C运行,x=2线程B运行,x=3线程B运行,x=4线程C运行,x=3线程C运行,x=4

注意!!!想在程序中真正启动多线程,必须依靠Thread类的方法public void start(),表示真正启动多线程,调用此方法后会间接调用run方法。

2.2 利用Runnable接口实现多线程

上面使用了Thread类来进行多线程的实现,但是缺点是单继承的问题。所以有了Runnable接口来实现多线程。

格式:

class 类名称 implements Runnable{                属性···                方法···       public void run(){            线程主体;//覆写Runnable接口里的run()方法             }}
上面写到启动线程我们需要用start()方法,但是start()方法是依靠的Thread类的,现在我们却使用的是Runnable接口,又该如何启动线程呢?别急,要解决这个问题还是要依靠Thread类完成,在Thread类中定义一个构造方法public Thread(Runnable target),通过此构造方法接收Runnable接口对象,并利用Thread类启动多线程。看例子吧:

MyThreadThread类

package com.test2;public class MyThreadThread implements Runnable{private String title;public MyThreadThread(String title){this.title=title;}@Overridepublic void run() {for(int x=0;x<5;x++){System.out.println(this.title+"运行,x="+x);}}}
启动线程类:

package com.test2;public class TestDemoThread {public static void main(String[] args) {MyThreadThread mtt1=new MyThreadThread("线程A");MyThreadThread mtt2=new MyThreadThread("线程B");MyThreadThread mtt3=new MyThreadThread("线程C");//启动线程new Thread(mtt1).start();new Thread(mtt2).start();new Thread(mtt3).start();}}

运行结果和上面一样就不在贴了。


2.3线程的操作状态

要想实现多线程,必须在主线程中创建新的线程对象。任何线程一般具有5中状态:创建、就绪、运行、阻塞和终止。

2.3.1 创建状态

  新建线程对象可采用Thread类的构造方法来实现,如Thread thread=new Thread()。创建线程对象后,新的线程对象便处于新建状态,此时,已经有了相应的内存空间和其他资源,但还处于不可运行的状态。

2.3.2 就绪状态

 调用start()方法可以启动线程了。线程启动时,即进入就绪状态。此时,线程将进入线程队列排队,等待CPU服务,这表明它已经具备了运行条件。

2.3.3 运行状态

  当就绪状态的线程被调用并获得处理器资源时,线程就进入了运行状态。此时,自动调用该线程对象的run()方法。run()方法定义了该线程的操作和功能。

2.3.4 堵塞状态

 正在运行的线程在某些情况下,如被人为挂起或需要执行耗时的输入输出操作时,将让出CPU并暂时中止自己的执行,进入堵塞状态。在可执行状态下,如果调用sleep()、suspend()、wait()等方法,线程都将进入堵塞状态。堵塞时,线程不能进入排队队列,只有当引入的堵塞原因被消除后,线程才可以转入就绪状态。

2.3.5 中止状态

线程调用stop()方法或run()方法执行结束后,线程即处于终止状态。此时,线程不具有继续运行的能力。


三、线程的主要操作方法

3.1 线程的命名和取得

线程的运行不可见,每次操作也是无法预料的,想要在程序中操作线程,唯一依靠的就是线程名称,取得和设置线程的名称如下:

public Thread (Runnable target ,String name)  构造  实例化线程对象,接收Runnable接口子类对象,同时设置线程名称

public final void setName(String name)  普通 设置线程名称

public final String getName() 普通 取得线程名称


取得当前线程对象:public static Thread currentThread()

3.2 线程的休眠

Thread类中提供了sleep(long millis)方法令线程开启休眠。


3.3 线程的优先级

如运算符有优先级一样,在线程的操作中同样有着先后操作。

线程在运行前都会保持在就绪状态,之后哪个优先级高哪个就会先执行。

而设置线程的优先级就需要用到Thread类中的下面这些方法和常量:

No.方法和常量类型描述1public static final int MAX_PRIORITY常量最高优先级,数值为102public static final int NORM_PRIORITY常量中等优先级,数值为53public static final int MIN_PRIORITY常量最低优先级,数值为14public final void setPriority(int newPriority)普通设置线程优先级5public final int getPriority();普通取得线程优先级

四、线程的同步与死锁

理解同步的概念:多个操作在同一个时间段内只能有一个线程进行,其他线程要等待此线程完成后才可以继续执行。

如何做到令其他线程在外等待呢?——加锁。

比如你要上洗手间,开门却发现打不开(上了锁),里面正有人在使用该洗手间,你没有办法,只好在门外等待里面的人出来。等锁打开,里面的人出来后,你才可以继续进去。

Java中加锁的方式是:①使用同步代码块;②使用同步方法。两者都要使用synchronized关键字。

4.1使用同步代码块加锁

使用synchronized定义的代码块就是同步代码块。

synchronized(同步对象){     //代码...}

观测上面的代码,括号中必须设置一个同步对象。该对象理解为当前对象this。

4.2使用同步方法

private static final synchronized 方法返回值类型 方法名称(参数) [throws异常]{    [return[返回值]];}

4.3死锁

两个线程都在等待彼此先完成,造成了程序的停滞。


线程间的操作有一个经典案例——生产者消费者。参考这篇文章第7条。(传送门)


五、线程的生命周期


新的方法:(不推荐使用,已经渐渐废除掉了,知道就好)

①suspend()方法:暂时挂起线程

②resume()方法:恢复挂起的线程

③stop():停止线程


正确的停止一个线程的方式使用标志位,定义一个boolean型的flag,然后新建一个方法

public void stop(){   this.flag = false;//修改标志位}

run方法中判断flag进而是否启动线程



0 0