1:理解线程的概念之前,我们有必要先理解一下进程的概念
程序(Program)是为实现特定目标或解决特定问题而用计算机语言(比如Java语言)编写的命令序列的集合。
进程指一个程序的一次执行过程
2:线程
线程又称为轻量级进程,线程是一个程序中实现单一功能的一个指令序列,是一个程序的单个执行流,存在于进程中,是一个进程的一部分。线程不能单独运行,必须在一个进程之内运行。
线程的特点
线程和进程
如何自定义线程类
线程中的进程
线程中的常用方法
importjava.util.Date;class TimeThread extendsThread{
@Overridepublic voidrun() {for(int i=0;i<=2; i++){
System.out.println("时间线程:"+newDate());try{
Thread.sleep(10000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}class CounterThread extendsThread {privateTimeThread timeThread;publicCounterThread(TimeThread timeThread){this.timeThread =timeThread;
}
@Overridepublic voidrun() {for(int i=1;i<=3; i++){if(i==2){try{
timeThread.join();
}catch(InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("计数器线程:"+i);
}
}
}public classProgram {public static voidmain(String[] args) {
TimeThread timeThread= newTimeThread();
timeThread.start();newCounterThread(timeThread).start();
}
}
注意:线程对象在调用join方法前必须先调用start方法,否则该线程永远不会进入执行状态。
importjava.text.SimpleDateFormat;importjava.util.Date;class TimeThread extendsThread {public voidrun() {
SimpleDateFormat sdf= new SimpleDateFormat("HH:mm:ss:sss");
String beforeTime= sdf.format(newDate());
System.out.println("beforeTime:"+beforeTime);try{
sleep(30000);//30秒后执行后面代码
} catch(Exception e) {
System.out.println("程序捕获了InterruptedException异常!");
}
String afterTime= sdf.format(newDate());
System.out.println("afterTime:"+afterTime);
}
}public classProgram {public static voidmain(String[] args) {
TimeThread timeThread= newTimeThread();
timeThread.start();try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
timeThread.interrupt();
}
}
class CounterThread extendsThread {
Object lockObj;publicCounterThread(Object lockObj) {this.lockObj =lockObj;
}
@Overridepublic voidrun() {synchronized(lockObj) {
System.out.println("计数器线程正在执行......");try{
lockObj.wait();//当线程执行该行代码后,线程进入阻塞状态;但由于10秒后主线程执行了“counterThread.interrupt();”代码使得该线程阻塞状态结束
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}public classTest {public static voidmain(String[] args) {
Object lockObj= newObject();
CounterThread counterThread= newCounterThread(lockObj);
counterThread.start();try{
Thread.sleep(10000);
}catch(InterruptedException e) {
e.printStackTrace();
}
counterThread.interrupt();
}
}
importjava.util.Date;class TimeThread extendsThread{
@Overridepublic voidrun() {for(int i=0;i<=2; i++){
System.out.println("时间线程:"+newDate());try{
Thread.sleep(10000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}class CounterThread extendsThread {privateTimeThread timeThread;publicCounterThread(TimeThread timeThread){this.timeThread =timeThread;
}
@Overridepublic voidrun() {for(int i=1;i<=3; i++){if(i==2){try{
timeThread.join();
}catch(InterruptedException e) {
System.out.println("计数器线程提前结束阻塞状态");
}
}
System.out.println("计数器线程:"+i);
}
}
}public classProgram {public static voidmain(String[] args) {
TimeThread timeThread= newTimeThread();
timeThread.start();
CounterThread counterThread= newCounterThread(timeThread);
counterThread.start();try{
Thread.sleep(15000);
}catch(InterruptedException e) {
e.printStackTrace();
}
counterThread.interrupt();//计数器线程执行该行代码后进入阻塞状态,时间线程至少需要消耗30秒才能结束,而15秒后计数器线程调用了interrupt方法致使该计数器线程提前结束阻塞状态。
}
}
守护线程不是将原来线程改为守护线程,而是本来就是守护线程,别忘了setDaemon方法需要在start方法之前调用。
代码1:public classProgram {public static voidmain(String[] args) {
CounterThread counterThread= newCounterThread();
counterThread.setDaemon(true);
counterThread.start();try{
Thread.sleep(1);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}class CounterThread extendsThread {public voidrun() {int i=1;while(true){
System.out.println("计数器:"+i);
i++;
}
}
}
代码2:importjava.text.SimpleDateFormat;importjava.util.Date;/*** 线程中所启动的其他非守护线程线程不会随着该线程的结束而结束*/
public classThreadDead {public static voidmain(String[] args) {try{
Thread.sleep(10000);
}catch(InterruptedException e) {
e.printStackTrace();
}
TimeThread timeThread= newTimeThread();
timeThread.start();//10秒后“任务管理器”中javaw.exe进程中线程数量会多一条
}
}class TimeThread extendsThread{
@Overridepublic voidrun() {
SimpleDateFormat sdf= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String currentTime= sdf.format(newDate());
System.out.println("时间线程,当前时间:"+currentTime);try{
Thread.sleep(10000);
}catch(InterruptedException e) {
e.printStackTrace();
}
CounterThread counterThread= newCounterThread();//counterThread.setDaemon(true);
counterThread.start();//10秒后“任务管理器”中javaw.exe进程中线程数量会再多一条
try{
Thread.sleep(5000);//一个时段后“任务管理器”中javaw.exe进程中线程数量会少一条,但计数器线程依然在工作
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}class CounterThread extendsThread {public voidrun() {int i=1;while(true){
System.out.println("计数器线程:"+i);
i++;try{
Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
终止程序——无疾而终
classCounterThread extends Thread {private boolean flag = true;public voidstopThread() {
flag= false;
}public voidrun() {int i = 0;while(flag) {
System.out.println("计数器线程:" +i);
i++;try{
sleep(5000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}public classProgram {public static voidmain(String[] args) {
CounterThread counterThread= newCounterThread();
counterThread.start();try{
Thread.sleep(15000);//15秒后线程终止
counterThread.stopThread();
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
终止程序——暴毙死亡
classCounterThread extends Thread {
@Overridepublic voidrun() {int i = 0;try{while (true) {
System.out.println("计数器线程:" +i);
i++;
Thread.sleep(3000);
}
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}public classProgram {public static voidmain(String[] args) {
CounterThread counterThread= newCounterThread();
counterThread.start();try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
counterThread.interrupt();
}
}
上面CounterThread类不可这样写:classCounterThread extends Thread {
@Overridepublic voidrun() {int i=0;while(true){
System.out.println("计数器线程:"+i);
i++;try{
Thread.sleep(3000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
串行运行
代码1:public classTest {private static Object shareData = new Object();//多线程间共享的数据
public static voidmain(String[] args) {new CounterThread("线程1",shareData).start();new CounterThread("线程2",shareData).start();
}
}classCounterThread extends Thread{privateObject shareData;publicCounterThread(String threadName,Object shareData){
super(threadName);this.shareData =shareData;
}
@Overridepublic voidrun() {
synchronized (shareData) {for (int i = 0; i < 3; i++) {
System.out.println(getName() + ":" +i);
}
}
}
}
代码2:public classTest {public static voidmain(String[] args) {new CounterThread("线程1").start();new CounterThread("线程2").start();
}
}classCounterThread extends Thread {publicCounterThread(String threadName) {
super(threadName);
}
@Overridepublic voidrun() {
synchronized (CounterThread.class) {//执行代码3可知道为什么这样做也可以
for (int i = 0; i < 3; i++) {
System.out.println(getName() + ":" +i);
}
}
}
}
代码3:public classTest {public static voidmain(String[] args){
System.out.println(Test.class == Test.class);
}
}
线程之间数据共享——并行运行
多个线程之间默认并发运行,这种运行方式往往会出现交叉的情况
线程之间数据共享——串行运行(synchronized)
使原本并发运行的多个线程实现串行运行,即多线程间同步执行,需要通过对象锁机制来实现,synchronized就是一个利用锁实现线程同步的关键字。
多线程同步原理
为什么通过synchronized就能实现多线程间串行运行呢?
被synchronized括着的部分就是线程执行临界区,每次仅能有一个线程执行该临界区中的代码:当多个线程中的某个线程先拿到对象锁, 则该线程执行临界区内的代码,其他线程只能在临界区外部等待,当此线程执行完临界区中的代码后,在临界区外部等待的其他线程开始再次竞争以获取对象锁,进而执行临界区中的代码,但只能有一条线程“胜利”。
临界区中的代码具有互斥性、唯一性和排它性:一个线程只有执行完临界区中的代码另一个线程才能执行。
1 packagecom.xt.two;2 import java.text.*;3 importjava.util.Date;4
5 public classSynTest {6
7 public static voidmain(String[] args) {8 Object lockObj = newObject();9 newDisplayThread(lockObj).start();10 }11 }12
13 class DisplayThread extendsThread {14
15 Object lockObj;16
17 publicDisplayThread(Object lockObj) {18 this.lockObj =lockObj;19 }20
21 @Override22 public voidrun() {23 synchronized(lockObj) {24 newTimeThread(lockObj).start();25 try{26 sleep(60000);27 } catch(InterruptedException e) {28 e.printStackTrace();29 }30 }31 }32 }33 class TimeThread extendsThread {34
35 Object lockObj;36
37 publicTimeThread(Object lockObj) {38 this.lockObj =lockObj;39 }40
41 @Override42 public voidrun() {43 System.out.println("时间线程开始执行......");44 synchronized(lockObj) {45 DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");46 String time = dateFormat.format(newDate());47 System.out.println(time);
//为什么这行代码60秒左右才会执行?48 //显示器线程和时间线程共享lockObj对象,显示器线程优先进入启动状态,随后执行相应的run方法,当执行同步代码块时lockObj变量所代表的对象锁归显示器线程所有,49 //进而创建时间线程并使之处于启动状态,此时有一下两种状态:50 //1、时间线程马上进入执行状态,51 //马上执行该时间线程run方法,可是由于此时lockObj变量所代表的对象锁被显示器线程持有,52 //这时时间线程进入阻塞状态,显示器线程再次执行,然后执行sleep方法,显示器线程在继续持有对象锁的前提下53 //也进入阻塞状态,60秒后显示器线程进入执行状态,随后显示器线程结束,对象锁被释放,进而时间线程开始执行,进而这行代码运行;54 //2、时间线程并没有马上进入执行状态,显示器线程执行sleep方法,显示器线程在继续持有对象锁的前提下55 //也进入阻塞状态,此时时间线程进入执行状态,执行该时间线程run方法,执行该方法中第一行输出代码,56 //可是由于此时lockObj变量所代表的对象锁被显示器线程持有,57 //所以时间线程并没有执行时间线程run方法内临界区中的代码,这时时间线程也进入阻塞状态,此时显示器和时间两条线程均进去阻塞状态,58 //等待少于60秒的时间后,显示器线程进入运行状态,随后显示器线程结束,对象锁被释放,进而时间线程开始执行,进而这行代码运行;
59 }60 }61 }
synchronized(this)
public classTest {public static voidmain(String[] args) {new CounterThread("线程1").start();new CounterThread("线程2").start();
}
}class CounterThread extendsThread{publicCounterThread(String threadName){super(threadName);
}
@Overridepublic voidrun() {synchronized (this) {//此时临界区中的代码无法实现串行执行,因为此时对象锁在线程1和线程2之间不共享
for (int i = 0; i < 3; i++) {
System.out.println(getName()+ " : " +i);
}
}
}
}
public classTest {public static voidmain(String[] args) {new Thread(new CounterThread(),"线程1").start();new Thread(new CounterThread(),"线程2").start();
}
}class CounterThread implementsRunnable {
@Overridepublic voidrun() {synchronized (this) {//此时临界区中的代码依然无法实现串行执行,因为每一个独立线程拥有一个独立的锁对象——new CounterThread()。//要明白这两点://谁调用该run方法?——CounterThread类对象;//谁执行该run方法?——正在执行的线程
Thread thread =Thread.currentThread();for (int i = 0; i < 3; i++) {
System.out.println(thread.getName()+ ":" +i);
}
}
}
}
public classTest {public static voidmain(String[] args) {
Runnable counterThread= newCounterThread();new Thread(counterThread,"线程1").start();new Thread(counterThread,"线程2").start();
}
}class CounterThread implementsRunnable {
@Overridepublic voidrun() {synchronized (this) {//此时临界区中的代码可以实现串行执行,因为此时接口实现类对象充当了对象锁的功能,该对象锁在两个线程之间共享
Thread thread =Thread.currentThread();for (int i = 0; i < 3; i++) {
System.out.println(thread.getName()+ ":" +i);
}
}
}
}
线程死锁
如果有两个或两个以上的线程都访问了多个资源,而这些线程占用了一些资源的同时又在等待其它线程占用的资源,也就是说多个线程之间都持有了对方所需的资源,而又相互等待对方释放的资源,在这种情况下就会出现死锁。 多个线程互相等待对方释放对象锁,此时就会出现死锁
public classDeadLockThread {//创建两个线程之间竞争使用的对象
private static Object lock1 = newObject();private static Object lock2 = newObject();public static voidmain(String[] args) {newShareThread1().start();newShareThread2().start();
}private static class ShareThread1 extendsThread {public voidrun() {synchronized(lock1) {try{
Thread.sleep(50);
}catch(InterruptedException e) {
e.printStackTrace();
}synchronized(lock2) {
System.out.println("ShareThread1");
}
}
}
}private static class ShareThread2 extendsThread {public voidrun() {synchronized(lock2) {try{
Thread.sleep(50);
}catch(InterruptedException e) {
e.printStackTrace();
}synchronized(lock1) {
System.out.println("ShareThread2");
}
}
}
}
}
线程协作
importjava.text.SimpleDateFormat;importjava.util.Date;public classElectronicWatch {
String currentTime;public static voidmain(String[] args) {new ElectronicWatch().newDisplayThread().start();
}/*** 该线程负责显示时间*/
class DisplayThread extendsThread{
@Overridepublic voidrun() {newTimeThread ().start();
System.out.println(currentTime);//为什么结果可能为null
}
}/*** 该线程负责获取时间*/
class TimeThread extendsThread{
@Overridepublic voidrun() {try{
sleep(3000);
}catch(InterruptedException e) {
e.printStackTrace();
}
String pattern= "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat sdf= newSimpleDateFormat(pattern);
currentTime= sdf.format(newDate());
}
}
}
代码2:packagetest;importjava.text.SimpleDateFormat;importjava.util.Date;public classElectronicWatch {
String currentTime;public static voidmain(String[] args) {new ElectronicWatch().newDisplayThread().start();
}/*** 该线程负责显示时间*/
class DisplayThread extendsThread{
@Overridepublic voidrun() {
TimeThread timeThread= newTimeThread();
timeThread.start();try{
sleep(5000);//这种方式不可取:这种方式性能不高
} catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(currentTime);
}
}/*** 该线程负责获取时间*/
class TimeThread extendsThread{
@Overridepublic voidrun() {try{
sleep(3000);
}catch(InterruptedException e) {
e.printStackTrace();
}
String pattern= "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat sdf= newSimpleDateFormat(pattern);
currentTime= sdf.format(newDate());
}
}
}
代码3:packagetest;importjava.text.SimpleDateFormat;importjava.util.Date;public classElectronicWatch {
String currentTime;public static voidmain(String[] args) {new ElectronicWatch().newDisplayThread().start();
}/*** 该线程负责显示时间*/
class DisplayThread extendsThread{
@Overridepublic voidrun() {
TimeThread timeThread= newTimeThread();
timeThread.start();try{
timeThread.join();
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(currentTime);
}
}/*** 该线程负责获取时间*/
class TimeThread extendsThread{
@Overridepublic voidrun() {try{
sleep(3000);
}catch(InterruptedException e) {
e.printStackTrace();
}
String pattern= "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat sdf= newSimpleDateFormat(pattern);
currentTime= sdf.format(newDate());
}
}
}
代码4:wait和notify方法
packagecom.xt.two;importjava.text.SimpleDateFormat;importjava.util.Date;public classElectronicWatch {
String currentTime;public static Object obj = newObject();public static voidmain(String[] args) {new ElectronicWatch().newDisplayThread(obj).start();
}/*** 该线程负责显示时间*/
class DisplayThread extendsThread{
Object obj;publicDisplayThread(Object obj) {this.obj =obj;
}
@Overridepublic voidrun() {
TimeThread tt=newTimeThread (obj);
tt.start();synchronized(obj) {try{
obj.wait();
}catch(InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(currentTime);//为什么结果可能为null
}
}/*** 该线程负责获取时间*/
class TimeThread extendsThread{
Object obj;publicTimeThread(Object obj) {this.obj =obj;
}
@Overridepublic voidrun() {try{
sleep(3000);
}catch(InterruptedException e) {
e.printStackTrace();
}
String pattern= "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat sdf= newSimpleDateFormat(pattern);
currentTime= sdf.format(newDate());synchronized(obj) {
obj.notify();
}
}
}
}