一、线程的基本概念
▶线程是一个程序内部的顺序控制流。
▶线程和进程的区别:
◆每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销。
◆线程可以看成是轻量级的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小。
◆多进程:在操作系统中能同时运行多个任务(程序)
◆多线程:在同一应用程序中有多个顺序流同时执行
▶Java的线程是通过 java.lang.Thread 类实现的
▶VM 启动时会有一个由主方法(public static void main(){})所定义的线程。
▶每个线程都是通过某个特定的 Thread 对象所对应的方法run()来完成其操作的,方法run()称为线程体。
▶通过调用 Thread 类的start()方法来启动一个线程。
二、线程的创建和启动
可以有两种方法创建新的线程
第一种
◇定义线程类实现 Runnable接口
◇Thread myThread = new Thread (Target) //target 为 Runnable 接口类型
◇Runnable 中只有一个方法
public void run ( ); 用以定义线程运行体。
◇使用 Runnable 接口可以为多个线程提供共享的数据。
◇在实现 Runnable 接口的类的 run 方法定义中可以使用 Thread 的静态方法:
public static Thread currentThread( ) 获取当前线程的引用
第二种
◇可以定义一个 Thread 的子类并重写其 run 方法如:
class MyThread extends Thread {
public void run( ){...}
}
◇然后生成该类的对象:
MyThread myThread = new MyThread(...)
例1:Runner1 r = new Runner1(); Thread t = new Thread(r);
两句如果换为 r.run( );就是先执行子线程的 run 方法,再执行主线程的 run 方法。
public class TestThread1 {
public static void main(String[] args) {
Runner1 r = new Runner1();
Thread t = new Thread(r);
t.start();//产生新分支,并交替运行
for(int i =0; i<100;i++) {
System.out.println("Main Thread:------"+i);
}
}
}
class Runner1 implements Runnable {//也可以直接继承Thread,但不推荐
public void run() {
for(int i = 0; i<100; i++) {
System.out.println("Runner1 :"+i);
}
}
}
三、线程的状态转换
四、线程控制基本方法
例1:
import java.util.*;
public class TestInterrupt {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
try {
Thread.sleep(10000);//sleep在哪儿用,哪儿睡
}catch(InterruptedException e) {
thread.interrupt();
}
thread.interrupt();
}
}
class MyThread extends Thread {
public void run() {
while(true) {
System.out.println("==="+new Date()+"===");
try {
sleep(1000);
}catch(InterruptedException e) {
return;
}
}
}
}
例2:Join合并线程
public class TestJoin {
public static void main(String[] args) {
MyThread2 t1 = new MyThread2("abcde");
t1.start();
try {
t1.join();
}catch(InterruptedException e) {}
for(int i=1; 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;
}
}
}
}
五、线程的优先级别
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程。
线程的优先级用数字表示,范围从1到10,一个线程的缺省优先级是5.
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 + 5);
t1.start();
t2.start();
}
}
class T1 implements Runnable {
public void run(){
for(int i=0; i<300; i++) {
System.out.println("T1: "+i);
}
}
}
class T2 implements Runnable {
public void run(){
for(int i=0; i<300; i++) {
System.out.println("====T2:"+i);
}
}
}
六、线程同步
◆在Java语言中,引入了对象互斥锁的概念,保证共享数据操作的完整性。每个对象都对应在一个可称为”互斥锁”的标记,这个标记保证在任一时刻,只能有一个线程访问该对象。
◆关键字 synchronized 来与对象的互斥锁联系。当某个对象 synchronized 修饰时,表明该对象在任一时刻只能由一个线程访问。
▶synchronized的使用方法如下面的Timer类,放在方法声明中表示整个方法为同步方法。
public class TestSync implements Runnable{
Timer timer = new Timer();
public static void main(String[] args) {
TestSync test = new TestSync();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test);
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
public void run() {
timer.add(Thread.currentThread().getName());
}
}
class Timer {
private static int num = 0;
public synchronized void add(String name) {
//synchronized(this) {//执行的过程中锁定当前对象
num++;
try {
Thread.sleep(1);
}catch(InterruptedException e) {}
System.out.println(name + ", 你是第"+num+"个使用timer的线程");
//}
}
}
例2:死锁
public class TestDeadLock implements Runnable {
public int flag = 1;
static Object o1 = new Object();
static Object o2 = new Object();
public void run() {
System.out.println("flag: "+flag);
if(flag == 1) {
synchronized(o1) {
try {
Thread.sleep(500);
}catch(Exception e) {
e.printStackTrace();
}
synchronized(o2) {
System.out.println("1");
}
}
}
if(flag == 0) {
synchronized(o2) {
try {
Thread.sleep(500);
}catch(Exception e) {
e.printStackTrace();
}
synchronized(o1) {
System.out.println("0");
}
}
}
}
public static void main(String[] args) {
TestDeadLock td1 = new TestDeadLock();
TestDeadLock td2 = new TestDeadLock();
td1.flag = 1;
td2.flag = 0;
Thread t1 = new Thread(td1);
Thread t2 = new Thread(td2);
t1.start();
t2.start();
}
}
例3:面试题。另外一个线程是完全可以访问没有锁定的方法的。
public class TT implements Runnable{
int b = 100;
public synchronized void m1() throws Exception {
b = 1000;
Thread.sleep(5000);
System.out.println("b =" + b);
}
public void m2() {
System.out.println(b);
}
public void run() {
try {
m1();
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) {
TT tt = new TT();
Thread t = new Thread(tt);
t.start();
try {
Thread.sleep(1000);
}catch(Exception e){}
tt.m2();
}
}
例4.生产者消费者问题
public class ProducerConsumer {
public static void main(String[] args) {
SyncStack ss = new SyncStack();
Producer p = new Producer(ss);
Consumer c = new Consumer(ss);
new Thread(p).start();
new Thread(c).start();
}
}
class woTou {
int id;
woTou(int id) {
this.id = id;
}
public String toString() {
return "woTou: "+ id;
}
}
class SyncStack {
int index = 0;
woTou[] arrWT = new woTou[6];
public synchronized void push(woTou wt) {
if(index == arrWT.length) {
try {
this.wait();
}catch(InterruptedException e) {
e.printStackTrace();
}
}
this.notify();
arrWT[index] = wt;
index ++;
}
public synchronized woTou pop(){
if(index == 0) {
try {
this.wait();
}catch(InterruptedException e) {
e.printStackTrace();
}
}
this.notify();
index --;
return arrWT[index];
}
}
class Producer implements Runnable {
SyncStack ss = null;
Producer(SyncStack ss) {
this.ss = ss;
}
public void run() {
for(int i =0; i<20; i++) {
woTou wt = new woTou(i);
ss.push(wt);
System.out.println("生产了: "+ wt);
try {
Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable {
SyncStack ss = null;
Consumer(SyncStack ss) {
this.ss = ss;
}
public void run() {
for(int i =0; i<20; i++) {
woTou wt = ss.pop();
System.out.println("消费了: "+ wt);
try {
Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}