提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
1.进程包含线程
2.线程比进程更轻量(创建快,销毁快)
3.同一个进程的多个线程之间共用一份内存资源; 进程和进程之间则是独立的内存资源
4.进程是资源分配的基本单位; 线程是调度执行的基本单位
多线程的创建有以下五种方式
class MyThread extends Thread{
//写一个MyThread类 继承自标准库中提供的Thread类
public void run(){
//重写run() 方法
//run() 方法里面的逻辑就是此线程要实现的工作
System.out.println("继承Thread类,重写Run方法,创建线程");
}
}
public class 多线程1 {
public static void main(String[] args) {
MyThread t1 = new MyThread();
//创建一个MyThread 实例(此时并未创建线程)
t1.start();
//启动线程,此时才会创建一个新的线程
}
}
优点:
1.把线程要执行的工作和线程本身分开(实现解耦合)*, 把线程要实现的工作放在了Runnable里面, 未来需要修改代码时(不用改动进程),只需要把Runnable传给其他实例,代码改动小
2.多个线程做同样的工作时使用Runnable更方便
3.更适合资源共享
class MyRunnable implements Runnable{
//来表示线程要实现的工作
public void run(){
System.out.println("实现Runnable接口,重写Run方法,创建线程");
}
}
public class 多线程1 {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread t2 = new Thread(runnable);
//通过Thread类的构造方法Thread(Runnable target) 构造出对象
t2.start();
//启动线程
}
}
public class 多线程1 {
public static void main(String[] args) {
Thread t3 = new Thread(){
public void run(){
System.out.println("使用匿名内部类,实现创建Thread类的子类,创建线程");
}
};
t3.start();
}
}
public class 多线程1 {
public static void main(String[] args) {
Thread t4 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("使用匿名内部类,实现Runnable接口,创建线程");
}
});
t4.start();
}
}
public class 多线程1 {
public static void main(String[] args) {
Thread t5 = new Thread(()->{
System.out.println("使用lambad表达式创建一个线程");
},"线程名字");
t5.start();
}
}
lambad表达式:本质上是一个"匿名函数"
() :函数的形参
-> :特殊语法,表示他是一个lambad表达式
{} :函数体
public void start()
使此线程开始执行; Java虚拟机调用此线程的run方法。
public void run()
:线程要完成的任务
(1) public static void sleep(long millis)
使线程阻塞一段时间(阻塞时间:millis 单位:毫秒)
(2) public static void sleep(long millis, int nanos)
使线程阻塞一段时间,可提高休眠精度至: (millis:毫秒,nanos:纳秒)
在run()方法中调用时会触发异常: java.lang.InterruptedException
(受查异常,手动处理)
解决方法:
Alt + 回车,选择Surround with try/catch
此时在main()方法中运行时也可选择try/catch
public static Thread currentThread()
返回当前线程对象所指向的引用
public class 多线程1 {
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println(thread.getName());
}
}
此时输出:main
(1) public void join()
:等待线程结束
(2) public void join(long millis)
:等待线程结束,最多等待millis毫秒
(3) public void join(long millis, int nanos)
:等待线程结束,最多等待millis毫秒nanos纳秒(提高了精度)
例如:
在mian线程中调用t.join
,意为等待线程t执行结束在继续执行main线程
线程的中断:提前让run()方法结束,而不是让run()方法执行到一半就强制结束
需要给标志位加入一个volatile
关键字
实例:
public class 线程中断1 {
private static class MyRunnable implements Runnable{
public volatile boolean isQuit = false;
//设置一个标志位,初始为false
public void run(){
while (!isQuit){
//标志位不为真时继续执行
System.out.println(Thread.currentThread().getName()+":交易中");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//让线程main阻1000毫秒
}
System.out.println(Thread.currentThread().getName()+":已经停止交易,这就跑路");
//run()方法执行结束
}
}
public static void main(String[] args) {
MyRunnable target = new MyRunnable();
//实例化MyRunnable
Thread t = new Thread(target,"小江");
//设置线程t名字为:小江
System.out.println(Thread.currentThread().getName()+":让小江开始交易");
t.start();
//调用线程t
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//让线程main阻塞3000毫秒
System.out.println(Thread.currentThread().getName()+":有内鬼停止交易!");
target.isQuit = true;
//将标志位更改为ture
}
}
方法:
public void interrupt()
:中断对象关联线程,如果正在阻塞则出发异常,否则设置标志位
public static boolean interrupted()
:判断当前线程是否设置标志位,调用后清除标志位
public boolean isInterrupted()
:判断当前线程是否设置标志位,调用后不清除标志位
示例:
public class 线程中断2 {
private static class MyRunnable implements Runnable{
//public volatile boolean isQuit = false;
public void run(){
while (!Thread.interrupted()){
//判断标志位是否存在,不存在则继续循环
System.out.println(Thread.currentThread().getName()+":交易中");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
//break;
throw new RuntimeException(e);
}
}
System.out.println(Thread.currentThread().getName()+":已经停止交易,这就跑路");
}
}
public static void main(String[] args) {
MyRunnable target = new MyRunnable();
Thread t = new Thread(target,"小江");
System.out.println(Thread.currentThread().getName()+":让小江开始交易");
t.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+":有内鬼停止交易!");
//target.isQuit = true;
t.interrupt();
//设置标志位
}
}
此时代码编译运行后会出现异常:
interrupt()
方法的行为有两种情况:
1.t 线程在运行状态时,会设置标志位为true;
2.t 线程在阻塞状态时,设置一个标志位又会随即清除,同时触发一个异常java.lang.InterruptedException
,这个异常会把sleep提前唤醒
解决方法:把run()方法中sleep的try/catch中的catch中写一个break;让其强行结束,即触发异常唤醒sleep后随即触发break;此时异常结束,循环也结束.
改变后的代码:
public class 线程中断1 {
private static class MyRunnable implements Runnable{
//public volatile boolean isQuit = false;
public void run(){
while (!Thread.interrupted()){
System.out.println(Thread.currentThread().getName()+":交易中");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
break;
//throw new RuntimeException(e);
}
}
System.out.println(Thread.currentThread().getName()+":已经停止交易,这就跑路");
}
}
public static void main(String[] args) {
MyRunnable target = new MyRunnable();
Thread t = new Thread(target,"小江");
System.out.println(Thread.currentThread().getName()+":让小江开始交易");
t.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+":有内鬼停止交易!");
//target.isQuit = true;
t.interrupt();
}
}
public final boolean isDaemon()
:判断此线程是否是后台线程/守护线程
返回为true就是后台线程/守护线程
默认创建的线程为"前台线程",可阻止进程退出(main就是"前台线程")