第8章 线程

线程可以让一个程序同时执行多个任务,在实际程序应用地非常广泛。

8.1 理解线程

8.1.1进程的概念

了解线程前先了解一下进程,进程可以看成是程序的执行,可以把一个程序的执行过程看成是一个进程。

比如我们打开一个Word程序,就有一个Word程序进程。

8.1.2 线程的概念(此处是个人理解。)

一个进程可以有多个线程,同时执行多个任务。同样用Word举例,Word显示可以看成是一个线程,编辑可以看出是一个线程。

8.1.3 线程的生命周期

线程的生命周期有4种基本状态:

1.产生

2.中断

3.非中断(运行或可运行)

4.退出

8.2 线程类设计

8.2.1 线程API类图

Thread类是线程实现的主类,其核心过程方法由Runnable接口规范。

 

8.2.2  线程类Thread的构造方法

1.Thread(String threadName);  //创建指定名称的线程类

2.Thread();                              //线程类名称系统自动指定Thread-编号

3.Thread(Runnable target)  //target为目标对象,target需要实现Runnable接口,给出run()方法。

 

8.3 线程实现

线程的实现一般有两种方式:

(1)创建Thread类的子类,并重新定义run()方法,用户只要创建子类对象就能建立自己的线程。

(2)创建实现Runnable接口的类,并实现run()方法。

 

8.3.1 继承实现

方式一:继承Thread类

public class ThreadDemo extends Thread{
public void run() {
//线程代码实现
System.out.println("线程执行");
}
public static void main(String[] args) {
ThreadDemo th = new ThreadDemo();
th.start();
}
}

8.3.2接口实现

方式二:实现Runnable接口

 

public class ThreadDemo2{
public static void main(String[] args) {
Thread th = new Thread(new MyThImpl());
th.start();
}
}
class MyThImpl implements Runnable{
public void run() {
//线程代码实现
System.out.println("线程执行");
}
}

 

8.4 线程控制

8.4.1 监控线程状态

线程状态在枚举类型Thread.State中定义。

NEW:新创建,还没调用start()

RUNNABLE:run()中代码被执行

BLOCKED:阻塞,等待解锁

WAITING:另一个线程运行,本线程等待

TIMED_WAIYING:等待到指定时间

TERMINATED:线程退出run()

获取线程状态方法:

public Thread.State getState()

 

8.4.2 线程休眠

Thread的sleep()方法

public class sleep1 {
public static void main(String[] args) throws InterruptedException {
for(int i=0;i<20;i++) {
System.out.println("main"+i);
Thread.sleep(1000);
}
}
}

注意:(1)sleep是静态方法,睡眠的是当前运行的线程、

 

8.4.3 中断线程

public void interrupt() :中断当前阻塞与等待状态,强制线程抛出异常。

 

8.4.4 阻塞进程 join()

void join(): 当前线程等待加入该线程后面,等待该线程终止

void join(long millis):当前线程等待该线程终止的时间最长为millis毫秒,如果超时还没执行完,当前线程也会进入就绪状态。

void join(long millis, int nanos);等待时间millis毫秒+nanos纳秒

8.4.5线程等待和唤醒

public final void wait() throws InterruptedException
public final void wait(long timeout,int nanos)throws InterruptedException
void notify()
void notifyAll()

如果线程等待用sleep(),唤醒用interrupt()

等待用join(),唤醒用interrupt()

等待用wait(),唤醒用notify();

 

8.4.6线程终止

当前线程放弃调度执行权力,等待下次排队执行。

public static void yield()

 

..线程控制综合实例

 

import java.util.Scanner;
public class sleep1 {
private static int data = -1;
private static Thread thread = new Thread(new Runnable() {
public void run() {
System.out.println("线程已经启动,输入0开始计数");
for(int i=10;i>0;i--) {
while(data != 0) {
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(100);
}catch(InterruptedException e) {
e.printStackTrace();
}

}
}
});
public static void main(String[] args) {
thread.start();
Scanner in = new Scanner(System.in);
while(data == -1) {
try {
data=Integer.parseInt(in.nextLine());
if(data!=0) {
System.out.println("enter 0 to start counting");
data = -1;
}
}catch(NumberFormatException e) {
System.out.println("请输入整数");
}
}
in.close();
}
}

8.5 线程属性

8.5.1 优先级

优先级较高的线程获取CPU资源的概率较大。 

每个线程默认的优先级和父线程相同。

Thread类提供setPriority(int newPriority和getPriority)和getPriority()方法来设置和得到优先级。

setPriority的参数时一个整数,范围1~10. (10最高)。

优先级实例:

public class priority1 {
public static void main(String[] args) {
new MyThread("低级",1).start();
new MyThread("高级",10).start();
}
}
class MyThread extends Thread{
public MyThread(String name,int pro) {
super(name);
setPriority(pro);
}
public void run() {
for(int i=0;i<=100;i++) {
System.out.println(this.getName()+"线程第"+i+"次执行");
}
}
}

多次运行可以发现,多数情况下优先级高的先运行完成。

8.5.2 守护线程

Java中有两类线程:用户线程User Thread 和 守护线程Daemon Thread

守护线程具有最低优先级,为系统其它对象提供服务。

一般不会自己操作守护进程。

 

8.6线程同步

8.6.1Synchronized同步方法和同步代码块

保护某段代码只能同时被一个线程执行的技术成为线程同步。

关键字synchronized是Java内部实现的一个简单锁,可以用synchronized锁定一段代码:

public static void biz() {
synchronized(Object.class) {
a++;
b++;
if(a!=b) {
System.out.println(a+"!="+b);
}
}
}

或者锁定整个方法:

synchronized public static void biz() {
a++;
b++;
if(a!=b) {
System.out.println(a+"!="+b);
}

}

 

8.6.2 同步变量volatile关键字

Java变量前可以加修饰语volatile,volatile的主要目的是解决成员变量的多线程访问同步问题。

在读取加了volatie修饰的变量时,会直接从内存读取。

但volatile不保证对变量的操作是原子操作。

Java提供java.util.concurrent.automic包,提供了基本类型与引用类型的原子性操作封装类:

AtomicBoolean

AtomicInteger

AtomicIntegerArray

...

 

8.6.3线程锁Lock接口及ReentrantLock类

...

8.6.4 死锁

不同线程都在等待那些不可能被释放的锁时,就成了死锁。

构成死锁的典型是锁交叉。