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