1.线程的基本概念
(1)线程是一个程序内部的顺序控制流。
(2)线程和进程的区别:
(a)每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销。
(b)进程可以看成是轻量级的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小。
(c)多进程:在操作系统中能同时运行多个任务(程序)。
(d)多线程:在同一个应用程序中有多个顺序流同时执行。
(3)Java的线程是通过java.lang.Thread类来实现的。
(4)VM启动时会有一个由主方法(public static void main(){})所定义的线程。
(5)可以通过创建Thread的实例来创建新的线程。
(6)每个线程都是通过某个特定Thread对象所相应的方法run()来完成其操作的,方法run()称为线程体。
(7)通过调用Thread类的start()方法来启动一个线程。
2.线程的创建和启动
(1)可以有两种方式创建新的线程:
第一种:
(a)定义线程类实现Runnable接口
(b)Thread myThread = new Thread(target)//target为Runnable接口类型。
(c)Runnable中只有一个方法:
public void run();//用以定义线程运行体。
(d)使用Runnable接口可以为多个线程提供共享数据。
(e)在实现Runnable接口的类的run方法定义中可以使用thread的静态方法:
public static Thread currentThread() 获取当前线程的引用。
第二种:
(a)可以定义一个Thread的子类并重写其run方法如:
class MyThread extends Thead{
public void run(){…}
}
(b)然后生成该类的对象:
MyThread myThread = new MyThread(…);
例子:
public class TestThread1 {
public static void main(String[] args) {
Runner1 r = new Runner1();
//方法调用
// r.run();
//线程启动1
// Thread t = new Thread(r);
// t.start();
//线程启动2
r.start();
for(int i=0;i<100;i++) {
System.out.println("Main Thread:------" + i);
}
}
}
//线程启动1---接口
//class Runner1 implements Runnable{
// public void run() {
// for(int i=0;i<100;i++) {
// System.out.println("Runner1 :" + i);
// }
// }
//}
//线程启动2---继承
class Runner1 extends Thread{
public void run() {
for(int i=0;i<100;i++) {
System.out.println("Runner1 :" + i);
}
}
}
注意:
两种线程启动方法,优先选择实现接口启动线程方法,因为接口比较灵活。
3.线程的调度和优先级
(1)Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程。线程调度器按照线程的优先级决定应调度哪个线程来执行。
(2)线程的优先级用数字表示,范围从1到10,一个线程的缺省优先级是5。
(a) Thread.MIN_PRIORITY = 1
(b) THread.MAX_PRIORITY = 10
(c) Thread.NORM_PRIORITY = 5
(3)使用下述线程方法获得或设置线程的对象的优先级:
(a) int getPriority();
(b) void setPriority(int newPriority);
例子:
public class TestPriority {
public static void main(String[] args) {
Thread t1 = new Thread(new T1());
Thread t2 = new Thread(new T2());
t1.setPriority(Thread.NORM_PRIORITY + 3);//把t1的优先级提高3
t1.start();
t2.start();
}
}
class T1 implements Runnable{
public void run() {
for(int i=0;i<1000;i++) {
System.out.println("T1: " + i);
}
}
}
class T2 implements Runnable{
public void run() {
for(int i=0;i<1000;i++) {
System.out.println("------T2: " + i);
}
}
}
4.线程的状态控制
(1)线程状态转换
创建—>start()—>就绪状态<–调度–>运行状态—>结束
运行状态—导致阻塞的事件—>阻塞状态—阻塞解除—>就绪状态
(2)基本方法:
isAlive 判断线程是否还“活着”,即线程在启动后还未终止
getPriority() 获得线程的优先级数值
setPriority() 设置线程的优先级数值
Thread.sleep 将当前线程睡眠指定毫秒数
join 调用某线程的该方法,将当前线程与该线程“合并”,即等待该线程结束,再恢复当前线程的运行。
yield() 让出CPU,当前线程进入就绪队列等待调度
wait() 当前线程进入对象的wait pool
notify/notifyAll() 唤醒对象的wait pool中的一个/所有等待线程
(3)sleep方法
(a)可以调用Thread的静态方法:
public static void sleep(long millis) throws InterruptedException
使得当前线程休眠(暂时停止执行millis毫秒)。
(b)由于是静态方法,sellp可以由类名直接调用:Thread.sleep(…)
例子:
import java.util.*;
public class TestInterrupt {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
try{
Thread.sleep(10000);
}catch(InterruptedException e) {
thread.interrupted();
}
}
}
class MyThread extends Thread{
boolean flag = true;
public void run() {
while(flag) {
System.out.println("===" + new Date() + "===");
try {
sleep(1000);
}catch(InterruptedException e) {
return;
}
}
}
}
注:在那个线程里调用sleep方法,就那个线程睡眠。
(4)join方法
合并某个线程
例子:
public class TestJoin {
public static void main(String[] args) {
MyThread2 t1 = new MyThread2("t1");
t1.start();
try {
t1.join();
}catch(InterruptedException e) {}
for(int i=0;i<=10;i++) {
System.out.println("i am main thread");
}
}
}
class MyThread2 extends Thread{
MyThread2(String s){
super(s);
}
public void run() {
for(int i=1;i<=10;i++) {
System.out.println("i am " + getName());
try {
sleep(1000);
}catch(InterruptedException e) {
return;
}
}
}
}
(5)yield方法
让出CPU,给其他线程执行的机会
例子:
public class TestYield {
public static void main(String[] args) {
MyThread3 t1 = new MyThread3("t1");
MyThread3 t2 = new MyThread3("t2");
t1.start();
t2.start();
}
}
class MyThread3 extends Thread{
MyThread3(String s){
super(s);
}
public void run() {
for(int i=0;i<=100;i++) {
System.out.println(getName() + ": " + i);
//每当i可以整除10的时候,则让出CUP位置给其他线程执行
if(i%10==0) {
yield();
}
}
}
}
5.线程同步
(1)在Java语言中,引入了对象互斥锁的概念,保证共享数据操作的完整性。每个对象都对应于一个可称为“互斥锁”的标记,这个标记保证在任一时刻,只能有一个线程访问该对象。
(2)锁定机制,在执行当前方法的过程中当前方法被锁定
synchronized(this){…}
或
public synchronized void add(String name){…}
6.补充
(1)同一个线程类可以启动两个线程
public class TestThread2 {
public static void main(String[] args) {
Runner2 r = new Runner2();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
}
}
class Runner2 implements Runnable{
public void run() {
for(int i=0;i<10;i++) {
System.out.println(i);
}
}
}
(2)线程的中断结束方法
public class TestThread4 {
public static void main(String[] args) {
Runner4 r = new Runner4();
Thread t = new Thread(r);
t.start();
for(int i=0;i<100;i++) {
if(i%10==0) {
System.out.println("in thread main i= " + i);
}
}
System.out.println("Thread main is over");
r.shutDown();
//t.stop();//不要用该方法
}
}
class Runner4 implements Runnable{
private boolean flag = true;
public void run() {
int i=0;
while(flag==true) {
System.out.println(" " + i++);
}
}
public void shutDown() {
flag = false;
}
}
(3)isAlive 方法
public class TestThread6 {
public static void main(String[] args) {
Thread t = new Runner6();
t.start();
for(int i=0;i<50;i++) {
System.out.println("MainThread: " + i);
}
}
}
class Runner6 extends Thread{
public void run() {
//currentThread拿到当前线程;isAlive判断线程是否“活着”,“活着”则true
System.out.println(Thread.currentThread().isAlive());
for(int i=0;i<50;i++) {
System.out.println("SubThread: " + i);
}
}
}
(4)wait和sleep之间的区别:
(a)wait 时,别的线程可以访问锁定对象。调用wait方法的时候必须锁定该对象。
(b)sleep 时,别的线程不可以访问锁定对象。