Java多线程编程核心技术阅读笔记(Java多线程技能)

1.1 Java进程和多线程的概念及线程的优点

线程可以理解成是在进程中独立运行的子任务 多线程可以最大限度地例用CPU的空闲时间来处理其它任务 比如一边让操作系统处理正在由打印机打印数据 一边使用WORD编辑文档 而CPU在这些任务之间不停地切换 由于切换速度非常快 给使用者的感受就是这些任务似乎在同时运行 单任务的特点就是排队执行 也就是同步 CPU利用率大幅度降低 在多任务的环境中 CPU可以完全在任务1和任务2之间来回切换 使任务2不必等到10秒再运行 系统的运行效率大大提升
在这里插入图片描述

1.2 使用多线程

一个程序正在运行时至少会有1个线程在运行

public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName());
    }

控制台输出的main就是一个名称为main的线程在执行main方法中的代码
在这里插入图片描述

1.2.1 继承Thread类

使用继承Thread类的方式创建线程时 最大的局限就是不支持多继承 因为Java不支持多继承 所以完全可以实现Runnable接口的方式 一边实现一边继承

public class MyThread extends Thread{
    @Override
    public void run() {
        super.run();
        System.out.println("myThread");
    }
}

MyThread myThread = new MyThread();
myThread.start();
System.out.println("运行结束");

在这里插入图片描述
从运行结果来看MyThread 的run方法执行比较晚 也就是说在使用多线程技术时 代码的运行结果与代码的执行顺序或调用顺序是无关的 线程启动顺序与START()执行顺序无关

1.2.2 实现Runnable接口

如果创建的线程类已经有一个父类 这时不能再继承Thread类了 因为Java不支持多继承 所以需要实现Runnable接口

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("runnable");
    }
}

new Thread(new MyRunnable()).start();
System.out.println("运行结束");
1.2.3 实例变量与线程安全

自定义线程类中的实例变量有共享与不共享之分 这在多个线程之间进行交互时是很重要的一个技术点

public class MyThread extends Thread{
    private int count = 5;
    public MyThread(String name){
        this.setName(name);
    }
    @Override
    public void run() {
        super.run();
       while (count > 0){
           count--;
           System.out.println("由" + Thread.currentThread().getName() + "计算.count=" + count);
       }
    }
}

        new MyThread("A").start();
        new MyThread("B").start();
        new MyThread("C").start();

一个创建了3个线程 每个线程都有各自的count变量 自己减少自己的count变量的值 不存在多个线程访问同一个变量的情况
在这里插入图片描述

共享数据就是多个线程访问同一个变量

public class MyThread extends Thread{
    private int count = 5;
    @Override
    public void run() {
        super.run();
           count--;
           System.out.println("由" + Thread.currentThread().getName() + "计算.count=" + count);
       }
    }
}
        MyThread myThread = new MyThread();
         Thread a = new Thread(myThread,"A");
         Thread b = new Thread(myThread,"B");
         Thread c = new Thread(myThread,"C");
         Thread d = new Thread(myThread,"D");
         Thread e = new Thread(myThread,"E");
         a.start();
         b.start();
         c.start();
         d.start();
         e.start();

线程C和B 打印得都是2 说明C和B同时对count进行了处理 产生了非线程安全问题 我们想要得结果应该是依次递减的
在这里插入图片描述
加上synchronized 关键字 重新运行就不会出现值一样的情况了 使用synchronized 关键字 在多个线程执行run方法时 以排队的方式进行处理 当一个线程调用run前 先判断run方法有没有上锁 如果上锁 说明有其它线程在使用该方法 必须等其它线程对run方法调用结束后才可以执行run synchronized 可以在任意地方加锁 而加锁的这段代码称为 “互斥区” 或 临界区

 private int count = 5;
    @Override
    synchronized  public  void run() {
        super.run();
        count--;
        System.out.println("由" + Thread.currentThread().getName() + "计算.count=" + count);
    }

在这里插入图片描述

1.3 currentThread方法

currentThread方法可返回代码段正在被哪个线程调用的信息

// main方法被名为main的线程调用
  public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName());
    }

1.4 isAlive 方法

判断当前线程是否处于活动状态 活动状态就是线程已经启动尚未终止 线程处于正在运行或准备开始运行的状态 就认为该线程是存活的

public static class BLogin extends Thread {
        @Override
        public void run() {
            System.out.println("run=" + this.isAlive());
        }
    }

  BLogin bLogin = new BLogin();
  System.out.println(bLogin.isAlive());
  bLogin.start();
  System.out.println(bLogin.isAlive());

在这里插入图片描述

1.5 sleep方法

让当前正在执行的线程休眠(暂停执行) 这个执行的线程是指 this.currentThread返回的线程

public class MThread1 extends Thread{
    @Override
    public void run() {
        try {
            System.out.println(this.currentThread().getName() + "begin");
            Thread.sleep(2000);
            System.out.println(this.currentThread().getName() + "end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static class Run1{
        public static void main(String[] args) {
            MThread1 mThread1 = new MThread1();
            System.out.println("begin=" + System.currentTimeMillis());
            mThread1.run();
            System.out.println("end=" + System.currentTimeMillis());
        }
    }
}

1.6 getId 方法

取得线程的唯一标识

    public static class Run1{
        public static void main(String[] args) {
            Thread thread = Thread.currentThread();
            System.out.println(thread.getName() + "->" + thread.getId());
        }
    }

1.7 停止线程

1.7.1 停止不了的线程

调用interrupt来停止线程

public class StopThread  extends Thread{
    @Override
    public void run() {
        super.run();
        for (int i = 0; i < 500000; i++){
            System.out.println("i=" + (i + 1));
        }
    }

    public static class Run{
        public static void main(String[] args) {
            try {
                StopThread thread = new StopThread();
                thread.start();
                Thread.sleep(2000);
                thread.interrupt();
            }catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

从运行结果来看 调用interrupt方法并没有停止线程
在这里插入图片描述

1.7.2 判断线程是否是停止状态

Thread类提供了2种方法

this.interrupt();  测试当前线程是否已经中断
this.isInterrupted(); 测试线程是否已经中断

先来看看this.interrupt()方法的解释 测试当前线程是否已经中断, 当前线程是指运行 this.intemipted方法的线程。

public class StopThread  extends Thread{
    @Override
    public void run() {
        super.run();
        for (int i = 0; i < 50000; i++){
            System.out.println("i=" + (i + 1));
        }
    }

    public static class Run{
        public static void main(String[] args) {
            try {
                StopThread thread = new StopThread();
                thread.start();
                Thread.sleep(1000);
                thread.interrupt();
                System.out.println("是否停止?" + thread.isInterrupted());
                System.out.println("是否停止?" + thread.isInterrupted());
            }catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

来判断 thread 对象所代表的线程是否停止 但从控制台打印的结果来看 线程并未停止这也就证明了 intcrmpted方法的解释:测试当前线程是否已经中断。 这个 “当前线程”是 main 它从未中断过 所以打印的结果是两个 false
在这里插入图片描述

    public static class Run{
        public static void main(String[] args) {
                Thread.currentThread().interrupt();
                System.out.println("是否停止?" + Thread.currentThread().isInterrupted());
                System.out.println("是否停止?" + Thread.currentThread().isInterrupted());

        }
    }

在这里插入图片描述
this.interrupt 测试当前线程是否已经是中断状态执行后具有将状态标志置清除为 false 的功能。
this.islnterrupted 测试线程Thread 对象是否已经是中断状态但不清除状态标志

1.7.3 能停止的线程(异常法)
public class StopThread  extends Thread{
    @Override
    public void run() {
        super.run();
        for (int i = 0; i < 5000000; i++){
            if(this.isInterrupted()){
                System.out.println("已停止 我要退出了");
                break;
            }
            System.out.println("i=" + (i + 1));
        }
    }

    public static class Run{
        public static void main(String[] args) {
            try {
                StopThread thread = new StopThread();
                thread.start();
                Thread.sleep(1000);
                thread.interrupt();
                System.out.println("end");
            }catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

虽然停止了线程 但如果for语句下面还有语句 还是会继续执行

   public void run() {
        super.run();
        for (int i = 0; i < 5000000; i++){
            if(this.isInterrupted()){
                System.out.println("已停止 我要退出了");
                break;
            }
            System.out.println("i=" + (i + 1));
        }
        System.out.println("我是FOR下面的语句");
    }

在这里插入图片描述
用异常终止线程

  public void run() {
        super.run();
        try {
            for (int i = 0; i < 5000000; i++){
                if(this.isInterrupted()){
                    System.out.println("已停止 我要退出了");
                    throw new InterruptedException();
                }
                System.out.println("i=" + (i + 1));
            }
            System.out.println("我是FOR下面的语句");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

在这里插入图片描述

1.7.4 在沉睡中停止

如果在 sleep 状态下停止某一线程 会进人 catch 语句 并且清除停止状态值 使之变成 false

public class SleepStop extends Thread{
    @Override
    public void run() {
        super.run();
        try {
            System.out.println("run");
            Thread.sleep(20000);
            System.out.println("run end ");
        } catch (InterruptedException e) {
            System.out.println("在沉睡中被停止" + this.isInterrupted());
            e.printStackTrace();
        }
    }

    public static class Run{
        public static void main(String[] args) {
            try {
                SleepStop thread = new SleepStop();
                thread.start();
                Thread.sleep(200);
                thread.interrupt();
            } catch (InterruptedException e) {
                System.out.println("main catch");
                e.printStackTrace();
            }
            System.out.println("end ");
        }
    }
}

在这里插入图片描述

1.7.5 暴力停止
public class StopThread1 extends Thread{
    private int i = 0;
    @Override
    public void run() {
        try {
            while (true){
                i++;
                System.out.println("i=" + i);
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public static class Run{
        public static void main(String[] args) {
            try {
                StopThread1 thread = new StopThread1();
                thread.start();
                Thread.sleep(8000);
                thread.stop();
                System.out.println("已停止");
            }catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

在这里插入图片描述
方法 stop()已经被作废, 因为如果强制让线程停止则有可能使一些清理性的工作得不到完成所以不建议在程序中使用 stop()方法

1.7.6 return 停止线程
public class ReturnStop extends  Thread{
    @Override
    public void run() {
        while (true){
            if(this.isInterrupted()){
                System.out.println("停止了");
                return;
            }
            System.out.println("timer" + System.currentTimeMillis());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ReturnStop returnStop = new ReturnStop();
        returnStop.start();
        Thread.sleep(2000);
        returnStop.interrupt();
    }
}

在这里插入图片描述
不过还是建议使用 “拋异常” 的方法来实现线程的停止 因为在 catch 块中还可以将异常向上拋 使
线程停止的事件得以传播

1.8 暂停线程

暂停线程意味着此线程还可以恢复运行

1.8.1 suspend与resume方法的使用
public class SuspendAndResume extends Thread{
    private long i = 0;
    public long getI(){
        return i;
    }

    public void setI(long i) {
        this.i = i;
    }

    @Override
    public void run() {
        while (true){
            i++;
        }
    }

    public static class Run{
        public static void main(String[] args) throws InterruptedException {
            SuspendAndResume thread = new SuspendAndResume();
            thread.start();
            thread.sleep(5000);
            // A
            thread.suspend();
            System.out.println("A=" + thread.getI());
            thread.sleep(5000);
            System.out.println("A=" + thread.getI());
            // B
            thread.resume();
            Thread.sleep(5000);
            thread.suspend();
            Thread.sleep(5000);
            System.out.println("B=" + thread.getI());
            Thread.sleep(5000);
            System.out.println("B=" + thread.getI());
        }
    }
}

在这里插入图片描述

1.8.2 suspend与resume方法的缺点-独占

在使用 suspend 与 resume 方法时如果使用不当极易造成公共的同步对象的独占 使
得其他线程无法访问公共同步对象。也容易出现因为线程的暂停而导致数据不同步的情况

1.9 yieId 方法

yicld()方法的作用是放弃当前的 CPU 资源将它让给其他的任务去占用 CPU 执行时
间但放弃的时间不确定 有可能刚刚放弃 马上又获得 CPU 时间片

public class YieIdDemo extends Thread{
    @Override
    public void run() {
         long beginTime = System.currentTimeMillis();
         int count = 0;
         for (int i =0 ; i< 500000; i ++){
            // Thread.yield();
             count = count + (i + 1);
         }
        long endTime = System.currentTimeMillis();
        System.out.println("用时:" + (endTime - beginTime) + "毫秒");
    }

    public static class Run{
        public static void main(String[] args) {
            YieIdDemo yieIdDemo = new YieIdDemo();
            yieIdDemo.start();
        }
    }
}

在这里插入图片描述
将yieId注释去掉 将CPU 让给其他资源导致速度变慢
在这里插入图片描述

1.10 线程的优先级

在操作系统中线程可以划分优先级,优先级较高的线程得到的 CPU 资源较多也就
是 CPU 优先执行优先级较高的线程对象中的任务。
线程的优先级分为 1 10 这 10 个等级, 如果小于 1 或大于 10, 则 JDK 拋岀异常 throw new IllegalArgumentException()

1.10.1 线程优先级的继承特性
public class ThreadPriority extends Thread{
    @Override
    public void run() {
        System.out.println("Thread1" + this.getPriority());
        ThreadPriority2 thread = new ThreadPriority2();
        thread.start();
    }

    public static class  ThreadPriority2 extends Thread{
        @Override
        public void run() {
            System.out.println("Thread2" + this.getPriority());
        }
    }

    public static  class Run{
        public static void main(String[] args) {
            System.out.println("mainThread->" + Thread.currentThread().getPriority());
            Thread.currentThread().setPriority(6);
            System.out.println("mainThread->" + Thread.currentThread().getPriority());
            ThreadPriority thread = new ThreadPriority();
            thread.start();
        }
    }
}

线程2继承线程1的优先级
在这里插入图片描述

1.10.2 优先级具有规则性
public class ThreadDemo extends Thread{
    @Override
    public void run() {
        long addResult = 0;
        for (int i = 0; i < 10; i++){
            for (int j = 0; j < 5000; j++){
                Random r = new Random();
                int i1 = r.nextInt();
                addResult+=i1;
            }
        }
        System.out.println("ThreadDemo1 " + addResult );
    }

    public static class ThreadDemo2 extends Thread{
        @Override
        public void run() {
            long addResult = 0;
            for (int i = 0; i < 10; i++){
                for (int j = 0; j < 5000; j++){
                    Random r = new Random();
                    int i1 = r.nextInt();
                    addResult+=i1;
                }
            }
            System.out.println("ThreadDemo2 " + addResult );
        }
    }

    public static  class Run{
        public static void main(String[] args) {
            for (int i = 0; i < 5 ; i++){
                ThreadDemo threadDemo = new ThreadDemo();
                threadDemo.setPriority(10);
                threadDemo.start();
                ThreadDemo2 threadDemo2 = new ThreadDemo2();
                threadDemo2.setPriority(1);
                threadDemo2.start();
            }
        }
    }
}

在这里插入图片描述
高优先级的线程总是大部分先执行完, 但不代表高优先级的线程全部先执行完 也就是 CPU 尽量将执行资源让给优先级比较高的线程。

1.10.3 优先级具有随机性

两个线程的优先级一个设置为 5, 另一个设置为 6, 让优先级接近一些

    public static  class Run{
        public static void main(String[] args) {
            for (int i = 0; i < 5 ; i++){
                ThreadDemo threadDemo = new ThreadDemo();
                threadDemo.setPriority(5);
                threadDemo.start();
                ThreadDemo2 threadDemo2 = new ThreadDemo2();
                threadDemo2.setPriority(6);
                threadDemo2.start();
            }
        }
    }

在这里插入图片描述

不要把线程的优先级与运行结果的顺序作为衡量的标准, 优先级较高的线程并不一定每一次都先执行完 run()方法中的任务, 也就是说,线程优先级与打印顺序无关,不要将这两者的关系相关联,它们的关系具有不确定性和随机性

1.11 守护线程

守护线程是一种特殊的线程 当进程中不存在非守护线程,则守护线程自动销毁。典型的守护线程就是垃圾回收线程,当进程中没有非守护线程了, 则垃圾回收线程也就没有存在的必要了, 自动销毁。任何一个守护线程都是整个 JVM 中所有非守护线程的 “保姆”, 只要当前 JVM实例中存在任何一个非守护线程没有结束,守护线程就在工作,只有当最后一个非守护线程结束时,守护线程才随着 JVM —同结束工作。

实现守护线程的方法是在线程start()之前setDaemon(true),否则会抛出一个IllegalThreadStateException异常

public class ThreadDemo3 extends Thread{
    private int i = 0;
    @Override
    public void run() {
        try {
            while (true){
                i++;
                System.out.println("i=" + i);
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static class Run{
        public static void main(String[] args) throws InterruptedException {
            ThreadDemo3 thread = new ThreadDemo3();
            thread.setDaemon(true); //默认为false,设置为false代表非守护线程,true为守护线程
            thread.start();
            Thread.sleep(5000);
            System.out.println("停止");
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值