多线程编程入门-Java

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 一、进程与线程的区别
  • 二、多线程的创建
    • 1.继承Thread类,重写Run方法
    • 2.实现Runnable接口,重写Run方法
    • 3.使用匿名内部类,实现创建Thread类的子类
    • 4.使用匿名内部类,实现Runnable接口
    • 5.使用lambad表达式
  • 三、多线程常用方法
    • 1.启动线程: start
    • 2.run
    • 3.sleep()
    • 4.currentThread
    • 5.等待一个线程: join
    • 6.中断一个线程
      • (1)直接定义一个标志位作为线程结束的标志
      • (2)使用标准库自带的标志位 interrupt()
    • 7.判断是否为后台线程


一、进程与线程的区别

1.进程包含线程
2.线程比进程更轻量(创建快,销毁快)
3.同一个进程的多个线程之间共用一份内存资源; 进程和进程之间则是独立的内存资源
4.进程是资源分配的基本单位; 线程是调度执行的基本单位

二、多线程的创建

多线程的创建有以下五种方式

1.继承Thread类,重写Run方法

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();
        //启动线程,此时才会创建一个新的线程
    }
}

2.实现Runnable接口,重写Run方法

优点:
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();
        //启动线程
    }
}

3.使用匿名内部类,实现创建Thread类的子类

public class 多线程1 {
    public static void main(String[] args) {
        Thread t3 = new Thread(){
            public void run(){
                System.out.println("使用匿名内部类,实现创建Thread类的子类,创建线程");
            }
        };
        t3.start();
    }
}

4.使用匿名内部类,实现Runnable接口

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();
    }
}

5.使用lambad表达式

public class 多线程1 {
    public static void main(String[] args) {
        Thread t5 = new Thread(()->{
            System.out.println("使用lambad表达式创建一个线程");
        },"线程名字");
        t5.start();
    }
}

lambad表达式:本质上是一个"匿名函数"
() :函数的形参
-> :特殊语法,表示他是一个lambad表达式
{} :函数体

三、多线程常用方法

1.启动线程: start

public void start()
使此线程开始执行; Java虚拟机调用此线程的run方法。

2.run

public void run() :线程要完成的任务

3.sleep()

(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

4.currentThread

public static Thread currentThread()返回当前线程对象所指向的引用

public class 多线程1 {
    public static void main(String[] args) {
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName());
    }
}

此时输出:main

5.等待一个线程: join

(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线程

6.中断一个线程

线程的中断:提前让run()方法结束,而不是让run()方法执行到一半就强制结束

(1)直接定义一个标志位作为线程结束的标志

需要给标志位加入一个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
    }
}

结果展示:
多线程编程入门-Java_第1张图片

(2)使用标准库自带的标志位 interrupt()

方法:
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();
        //设置标志位
    }
}

此时代码编译运行后会出现异常:
多线程编程入门-Java_第2张图片
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();
    }
}

此时输出正常:
多线程编程入门-Java_第3张图片

7.判断是否为后台线程

public final boolean isDaemon():判断此线程是否是后台线程/守护线程
返回为true就是后台线程/守护线程
默认创建的线程为"前台线程",可阻止进程退出(main就是"前台线程")

你可能感兴趣的:(java,算法,c++)