您的位置:首页 > 编程语言 > Java开发

Java-多线程

2016-03-11 10:35 549 查看

一、进程和线程

多进程:在操作系统中可以同时运行多个运用程序。

多线程:在同一个运用程序中可以有多个执行的路径。

进程和线程的区别:

进程是系统资源调度和分配的基本单位,线程是CPU调度和分配的基本单位。

一个线程只能属于一个进程,一个进程可以拥有多个线程,但是至少有一个线程(称为主线程,Android系统中也称为UI线程)。

进程之间不能共享内存,线程之间可以共享内存,但是在线程之间通信需要注意数据同步的问题。

二、线程



1、创建多线程

创建新执行线程有两种方法。一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。接下来可以分配并启动该子类的实例。

class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("MyThread1:" + i);
}
}
}
MyThread thread = new MyThread();
thread.start();


另一个方法是:3. 声明一个实现Runnable接口的子类,重写run()方法。使用的时候,声明一个Thread类的实例,把Runnable的子类对象传入就可以了。

class MyThread2 implements Runnable{

@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("MyThread2:"+i);
}
}
}
MyThread2 thread2 = new MyThread2();
new Thread(thread2).start();


在创建线程的时候可以传入一个指定线程名称的参数,如果没有指定线程名,则会默认一个线程名称为”Thread-0”(0表示子线程的数量,从0开始计数)。

问题:继承Thread类和实现Runnable接口的区别?

(1)实现Runnable接口可以解决单继承的局限问题;

(2)方便解决多个线程操作同一个数据的问题。

2、多线程的优先级

每个线程都有一个优先级,高优先级线程的执行优先于低优先级线程,所以对执行结果会有一定的影响。

public final static int MIN_PRIORITY = 1;//最低优先级
public final static int NORM_PRIORITY = 5;//正常优先级
public final static int MAX_PRIORITY = 10;//最大优先级


3、常用方法

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

public long getId();//返回该线程的标识符。线程 ID 是一个正的 long 数,在创建该线程时生成。线程 ID 是唯一的,并终生不变。

public final void setName(String name);//改变线程名称,使之与参数 name 相同
public final String getName();//返回该线程的名称

public final void setPriority(int newPriority);//更改线程的优先级。
public final int getPriority();//返回线程的优先级。

public final ThreadGroup getThreadGroup();//返回该线程所属的线程组。 如果该线程已经终止(停止运行),该方法则返回 null。

public final void join();//等待该线程终止。
public final void join(long millis);//等待该线程终止的时间最长为 millis 毫秒。
public final void join(long millis,int nanos);//等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。
join:插队。如果A线程要礼让B线程,需要在A线程中去调用B线程的join方法,才会起到插队的效果(主线程和子线程同时运行,满足一定条件后,让子线程先运行)。

public static void sleep(long millis);
public static void sleep(long millis,int nanos);//在指定的时间内让当前正在执行的线程休眠(暂停执行),该线程处于阻塞状态。

public static void yield();//暂停当前正在执行的线程对象,并执行其他线程。
Yield()方法是停止当前线程,让同等优先权的线程运行,如果没有同等优先权的线程,那么Yield()方法将不会起作用。

public final void setDaemon(boolean on);//将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时(没有了被守护者),Java 虚拟机退出。 该方法必须在启动线程前调用。

所谓守护线程,是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者。

public final boolean isDaemon();//测试该线程是否为守护线程

public String toString();//返回该线程的字符串表示形式,包括线程名称、优先级和线程组。

public void start();//使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
//结果是两个线程并发地运行;当前线程(从调用返回给 start 方法)和另一个线程(执行其 run 方法)。

public void run();//如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。

public final void stop();//(已过时)强迫线程停止执行。

public void interrupt();//中断线程

interrupt()中断线程。需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛出的。
对某一线程调用interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。
但是,一旦该线程进入到wait()/sleep()/join()后,就会立刻抛出InterruptedException

public static boolean interrupted();//测试当前线程是否已经中断
public boolean isInterrupted();//测试线程是否已经中断

public final void wait(long timeout)
public final void wait(long timeout,int nanos)
public final void wait();//在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。

public final void notify();//唤醒在此对象监视器上等待的单个线程。
public final void notifyAll();//唤醒在此对象监视器上等待的所有线程。线程通过调用其中一个 wait 方法,在对象的监视器上等待


例:设置线程的优先级,高优先级的线程会优先执行。

public class PriorityTest {

public static void main(String[] args) {
PriorityThread t1 = new PriorityThread("张三");
PriorityThread t2 = new PriorityThread("李四");
t1.start();
t2.start();
System.out.println(t1.getPriority()+":"+t2.getPriority());
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.MIN_PRIORITY);
System.out.println(t1.getPriority()+":"+t2.getPriority());
}

}
class PriorityThread extends Thread {
private String threadName;
public PriorityThread(String threadName){
super(threadName);
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+":" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public String getThreadName() {
return threadName;
}
public void setThreadName(String threadName) {
this.threadName = threadName;
}
}


输出结果:

张三:0
李四:0
设置前:t1=5:t2=5
设置后:t1=10:t2=1
张三:1
李四:1
张三:2
李四:2
李四:3
张三:3
张三:4
李四:4


结果可以看出线程”张三”比线程”李四”更优先执行

例:Sleep()休眠一段时间

public class SleepTest {

public static void main(String[] args) {
TimeThread t1 = new TimeThread();
TimeThread t2 = new TimeThread();
t1.start();
t2.start();
}

}
class TimeThread extends Thread{
@Override
public void run() {
while(true){
System.out.println(Thread.currentThread().getName()+":"+new Date());
try {
sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}
}


输出结果:

Thread-0:Wed Mar 16 22:08:33 CST 2016
Thread-1:Wed Mar 16 22:08:33 CST 2016
Thread-0:Wed Mar 16 22:08:34 CST 2016
Thread-1:Wed Mar 16 22:08:34 CST 2016
Thread-0:Wed Mar 16 22:08:35 CST 2016
Thread-1:Wed Mar 16 22:08:35 CST 2016
Thread-0:Wed Mar 16 22:08:36 CST 2016
Thread-1:Wed Mar 16 22:08:36 CST 2016


每个1s执行一次线程

例:Join,主线程礼让子线程

public class JoinTest {

public static void main(String[] args) {
JoinThread t1 = new JoinThread();
t1.start();
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
if (i == 2) {
try {
t1.join();//t1子线程优先执行完后,再执行主线程
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}

class JoinThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName() + ":" + i);
}
}
}


输出结果:

main:0
main:1
main:2
Thread-0:0
Thread-0:1
Thread-0:2
Thread-0:3
Thread-0:4
Thread-0:5
Thread-0:6
Thread-0:7
Thread-0:8
Thread-0:9
main:3
main:4
main:5
main:6
main:7
main:8
main:9


当主线程中i的值为2时,子线程优先执行完,然后再继续执行主线程

例:interrupt中断线程,线程进入到wait()/sleep()/join()后,会抛出interruptexception异常。

public class InterruptTest2 {

public static void main(String[] args) {
InterruptThread2 t = new InterruptThread2();
t.start();
t.interrupt();
}
}

class InterruptThread2 extends Thread {
@Override
public void run() {
System.out.println("开始");

System.out.println("ok");
try {
sleep(1000);
} catch (InterruptedException e) {
System.out.println("出现异常");
e.printStackTrace();
}

System.out.println("结束");
}
}


输出结果:

开始
ok
出现异常
结束
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.thread.InterruptThread2.run(InterruptTest2.java:19)


如果需要中断线程,一般设置一个标志位,在要中断的线程中改变这个标志位。

public class InterruptTest {

public static void main(String[] args) {
InterruptThread t = new InterruptThread();
t.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.setStop(false);//
//      t.interrupt();
}
}

class InterruptThread extends Thread {
private boolean flag = true;
@Override
public void run() {
while (flag) {
System.out.println("ok");
}
}

public void setStop(boolean flag) {
this.flag = flag;
}
}


关于线程的几个常用方法的使用,参考链接:

http://zheng12tian.iteye.com/blog/1233638

4、线程的生命周期



a) 新建状态

i. 在程序中用构造方法创建了一个线程对象后,新的线程对象便处于新建状态,此时,它已经有了相应的内存空间和其它资源,但还处于不可运行状态。新建一个线程对象可采用线程构造方法来实现。

ii. 例如:Thread thread=new Thread();

b) 就绪状态

i. 新建线程对象后,调用该线程的start()方法就可以启动线程。当线程启动时,线程进入就绪状态。此时,线程将进入线程队列排队,等待CPU调用,这表明它已经具备了运行条件。

c) 运行状态

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

d) 阻塞状态

i. 一个正在执行的线程在某些特殊情况下,如被人为挂起,将让出CPU并暂时中止自己的执行,进入阻塞状态。在可执行状态下,如果调用sleep(2000)、wait()等方法,线程都将进入阻塞状态。阻塞时,线程不能进入排队队列,只有当引起阻塞的原因被消除后,线程才可以转入就绪状态。

e) 死亡状态

i. 线程调用stop()方法时或run()方法执行结束后,线程即处于死亡状态。处于死亡状态的线程不具有继续运行的能力。//线程池 可以让线程起死回生
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息