第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 死锁
不同线程都在等待那些不可能被释放的锁时,就成了死锁。
构成死锁的典型是锁交叉。