《JAVA多线程编程核心技术》-- 第一章 -- java多线程技能

1.1 进程和多线程的概念以及线程的优点
  • 进程: 进程是操作系统进行资源分配和调度的基本单位(可以将windows中任务管理器中的每个exe程序理解为一个进程)
  • 线程: 线程可以理解为进行中独立运行的主任务
  • 例如: 电脑管家这个程序可以看做一个进程,电脑管家里的病毒查杀,清理垃圾等可以做为线程
    在这里插入图片描述
    单线程与多线程的区别:
    在这里插入图片描述
    相对于单线程,多线程可以同时处理多个任务,减少总的任务完成时间,这是多线程的优点,使用多线程可以理解为在使用异步.

注意: 多线程是异步的,所以千万不要把创建线程的顺序理解为线程的执行顺序,线程被调用的时机是随机的

1.2 使用多线程

一个进程在运行是至少有一个线程在运行,比如调用的main线程,是有jvm创建的.
在这里插入图片描述
注意: 在控制台输出的main与main方法没有任何关系,只是名字相同

1.2.1 继承Thread类

使用继承thread的方式创建线程时,最大的局限就是不支持多继承了,因为java规定类只能单继承,所以,一般推荐使用实现Runnable的方式创建线程.
在这里插入图片描述
执行的结果:
在这里插入图片描述
从结果可以看出,代码的运行结果与代码执行顺序或者调用顺序无关的
线程是一个子任务,CPU以不确定的方式,或随机的时间来调用线程中的run方法,所以才会出现上面这种结果与执行顺序不同的情况.

注意: 如果多次调用start(), 可能会出现异常: Exception in thread “main” java.lang.IllegalThreadStateException.

Thread.java中的start()方法通知"线程规划器",此时线程已经准备就绪,等待调用线程对象的run()方法,这个过程就是让系统安排一个时间调用thread中的run()方法,启动线程,具有异步的效果,但是如果直接调用thread.run()方法,就不是异步的了,而是像平常那样的普通方法,而是同步,不会把线程对象交给"线程规划器"来进行处理,而是由main线程调用run方法,也就是必须等run()方法中的代码执行完成后才能执行下一步的代码.

public class Test03 {
    public static void main(String[] args) {
        MyThread002 myThread002 = new MyThread002(1);
        MyThread002 myThread003 = new MyThread002(2);
        MyThread002 myThread004 = new MyThread002(3);
        MyThread002 myThread005 = new MyThread002(4);
        MyThread002 myThread006 = new MyThread002(5);
        MyThread002 myThread007 = new MyThread002(6);
        MyThread002 myThread008 = new MyThread002(7);
        MyThread002 myThread009 = new MyThread002(8);

        myThread002.start();
        myThread003.start();
        myThread004.start();
        myThread005.start();
        myThread006.start();
        myThread007.start();
        myThread008.start();
        myThread009.start();

    }
}

class MyThread002 extends Thread{
    private int i = 0;
    public MyThread002(int i){
        this.i = i;
    }
    @Override
    public void run() {
        System.out.println("创建线程 : " + this.i);
    }
}

执行结果 :
在这里插入图片描述
所以,执行start() 的顺序并不是线程启动的顺序.

1.2.2 实现Runnable接口

使用方式:

public class Test04 {
    public static void main(String[] args) {
        MyThread004 myThread004 = new MyThread004();
        Thread thread = new Thread(myThread004);
        thread.start();
        System.out.println("线程结束");

// 2. 方式2
MyThread004 myThread005 = new MyThread004();
Thread thread2 = new Thread(myThread005,"线程2");
thread2.start();
System.out.println("线程结束");
    }
}

class MyThread004 implements Runnable{
    @Override
    public void run() {
        System.out.println("通过runnable 创建线程");
    }
}
1.2.3 实例变量和线程安全

线程中的实例变量分为两种:

  1. 不共享的数据
    每个线程有字节的值,每次操作也只针对自己的数据,这种情况就是变量的不共享
public class Test05 {
    public static void main(String[] args) {
        MyThread005 myThread005 = new MyThread005("a");
        MyThread005 myThread006 = new MyThread005("b");
        MyThread005 myThread007 = new MyThread005("c");

        myThread005.start();
        myThread006.start();
        myThread007.start();
    }

}
class MyThread005 extends Thread{
    private int i = 5;
    public MyThread005(String name){
        super();
        this.setName(name);
    }
    @Override
    public void run() {
        super.run();
        while (i > 0){
            i--;
            System.out.println("当前值是 : " + Thread.currentThread().getName() +"===" + this.i);
        }
    }
}
  1. 共享的数据
    多个线程访问同一个变量
public class Test06 {
    public static void main(String[] args) {
        MyThread006 myThread005 = new MyThread006();
        Thread thread = new Thread(myThread005,"a");
        Thread thread2 = new Thread(myThread005,"b");
        Thread thread3 = new Thread(myThread005,"c");
        Thread thread4 = new Thread(myThread005,"e");
        Thread thread5 = new Thread(myThread005,"f");

        thread.start();
        thread2.start();
        thread3.start();
        thread4.start();
        thread5.start();
    }

}

class MyThread006 extends Thread{
    private int i = 5;
    @Override
    public void run() {
        super.run();
        while (i > 0){
            i--;
            System.out.println("当前值是 : " + Thread.currentThread().getName() +"===" + this.i);
        }
    }
}

在 jvm中, i–的操作步骤是 : 获取原有的值,计算i-1,对i进行赋值
多个线程对于同一个变量进行操作,容易产生数据安全问题,可能出现错误,错误的原因是对于同一个值由两个以上的线程同时进行操作了,进而导致数据不同步,影响执行流程.所以,解决这种情况的方式是: 线程按照顺序操作共享数据中的值.

就需要用到: synchronized :
当线程执行到这个方法事,先获取锁,如果获取到锁,执行方法,如果没有获取锁,等待锁释放后再获取锁,同一时间只有一个线程获取到锁.
synchronized 可以在任意对象及方法上加锁,加锁的这段代码称之为 “互斥区” 或 “临界区”

1.2.4 留意i++ 与 System.out.println() 的异常

从上一章可以知道,解决线程安全问题的方式之一是: synchronized关键字,查看println() 源码可以知道这个方法使用了这个关键字,当i++与pirntln同时使用时,仍然有可能出现线程安全问题,原因是:虽然println方法内部是同步的,但是i++的操作确是在进入println方法之前发生的,所以仍然有一定的概率出现线程安全问题.

1.3 currentThread()方法

currentThread()方法返回代码块正在被那个线程执行的信息

class MyThread2 extends Thread {
    public MyThread2() {
        System.out.println("MyThread 构造器 begin");
        System.out.println("当前线程状态 构造器 : " + Thread.currentThread().getState());
        System.out.println("Thread.currentThread().getName() : " + Thread.currentThread().getName());
        System.out.println("Thread.currentThread().isAlive() : " + Thread.currentThread().isAlive());
        System.out.println("this.isAlive : " + this.isAlive());
        System.out.println("this.getName :" + this.getName());
        System.out.println("MyThread 构造器 end");
    }

    @Override
    public void run() {
        System.out.println("-------------------------------------------------");
        System.out.println("MyThread (run) begin");
        System.out.println("当前线程状态 (run) : " + Thread.currentThread().getState());
        System.out.println("Thread.currentThread().getName() (run) : " + Thread.currentThread().getName());
        System.out.println("Thread.currentThread().isAlive() (run) : " + Thread.currentThread().isAlive());
        System.out.println("this.isAlive (run) : " + this.isAlive());
        System.out.println("this.getName (run) :" + this.getName());
        System.out.println("MyThread (run) end");
    }
}

测试方法是:

/**
 * 线程的存活, 指的是正在运行或者准备好运行的状态
 */
private static void aboutThreadIsAlive() throws InterruptedException {
    MyThread2 myThread = new MyThread2();
    Thread thread = new Thread(myThread);
    System.out.println("--------------------------------------");
    System.out.println("begin : " + thread.isAlive());
    System.out.println("当前线程状态 : " + thread.getState());
    myThread.setName("线程0");
    thread.setName("线程1");
    thread.start();
    Thread.sleep(1000);
    System.out.println("--------------------------------------");
    System.out.println("end : " + thread.isAlive());
}

结果是:
在这里插入图片描述
从上可以看出, 线程类的构造方法是由主线程调用的,而run()方法是由新创建的线程调用的.

1.4 isAlive()方法

isAlive() 方法的功能是判断当前线程是否处于活动状态,所谓活动状态,指的是线程正处于运行或准备运行状态,已经启动并且尚未终止.
1.3中的 System.out.println(“end : " + thread.isAlive()); 打印的值是false,是因为上有个sleep()方法,如果没有这个方法,返回的有可能是true,这是因为thread的线程还没有结束,当执行sleep()后,就不是运行或者准备运行状态,所以不是"存活”,也就是false.

1.5 sleep()方法

sleep() 方法的作用是在指定毫秒内让"当前正在执行的线程"休眠(暂停执行),这个"正在执行的线程"是指this.currentThread()返回的线程.

public class Test07 {
    public static void main(String[] args) {
    MyThread007 myThread007 = new MyThread007();
        System.out.println("begin : " + System.currentTimeMillis());
        myThread007.start();
        System.out.println("end : " + System.currentTimeMillis());
    }
}

class MyThread007 extends Thread{
    private int i = 5;
    @Override
    public void run() {
        super.run();
        for (int j = 0; j < 10; j++) {
            System.out.println("打印 " + j);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

执行结果:
在这里插入图片描述
由于main线程与thread线程是异步的,所以先打印main线程,然后随机的时间后打印thread中的内容.

1.6 getId()方法

getId()方法获取线程的唯一id;

public class Test08 {
    public static void main(String[] args) {
        System.out.println("id是 : " + Thread.currentThread().getId());
    }
}

结果是 :
在这里插入图片描述

1.7 停止线程

停止线程在java语言中并不像break语句那样干脆,需要一些技巧性的处理.
如果处理不好,将会导致超出预期的行为并且难以定位的错误.
停止线程意味着在线程处理完成之前停止正在做的操作,虽然看着简单,但是要做好防范措施,才能得到预期的结果.
停止一个线程可以使用stop().但最好不用他,虽然它确是可以停止一个正在运行的线程,但是这个方法是不安全的,而且已经被废弃了.
大多数停止线程的方法是使用Thread.interrupt()方法,虽然这个方法是"中断",但是实际上这个方法并不会终止一个正在执行的方法,还需要加入一个判断才能完成线程的终止.

终止线程的方法有三种:

  1. 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止,
  2. 使用stop()方法强行终止线程,但是不推荐,因为stop() 和 suspend() 方法和 resume()方法一样,都是过期作废的方法,使用它们可能会造成不可预知的后果.
  3. 使用interrupt()方法中断线程.
1.7.1 停止不了的线程
public class Test09 {
    public static void main(String[] args) throws InterruptedException {
        MyThread009 myThread009 = new MyThread009();
        myThread009.start();
        Thread.sleep(1000);
        Thread.interrupted();
    }
}
class MyThread009 extends Thread{
    @Override
    public void run() {
        super.run();
        for (int i = 0; i < 100; i++) {
            System.out.println("i=" + (i+1));
        }
    }
}

结果:
在这里插入图片描述
运行的结果: 线程并没有结束;

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

在thread.java中提供两个api用来判断线程状态:

  1. this.interrupted() 测试当前线程是否已经中断, 并移除线程的中断状态
public class Test10 {
    public static void main(String[] args) {
        try {
            MyThread10 myThread10 = new MyThread10();
            myThread10.start();
            // 中的线程
            Thread.interrupted();
            // 查看线程是否终止
            System.out.println("线程是否停止1 : " + Thread.interrupted());
            System.out.println("线程是否停止2 : " + Thread.interrupted());
        } catch (Exception e) {
            System.out.println("main catch");
            e.printStackTrace();
        }
        System.out.println("end");
    }
}
class MyThread10 extends Thread{
    @Override
    public void run() {
        super.run();
        for (int i = 0; i < 10; i++) {
            System.out.println("i=" + (i+1));
        }
    }
}

结果是 :
在这里插入图片描述
从上面结果可以看出,线程并未停止,但是自定义的线程已经停止了,所以,这个方法中的当前线程应该是调用这个方法的当前线程,在这个方法中应该是main线程.

测试主线程:

public class Test10 {
    public static void main(String[] args) {
        Thread.currentThread().interrupt();
        // 查看线程是否终止
        System.out.println("线程是否停止1 : " + Thread.interrupted());
        System.out.println("线程是否停止2 : " + Thread.interrupted());
    }}
    ```
运行结果是: 
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190414130823307.png)
为什么第一个是true,但是第二个是false?
是因为 Thread.interrupted() 这个方法测试当前状态是否已中断,并且移除当前的中断状态,所以,第一个的时候确定了是中断状态,然后移除中断状态,第二个的时候就不是中断状态,也就是false了.

2. this.isInterrupted()  测试线程是否已经中断 ,不移除线程的中断状态

public class Test10 {
public static void main(String[] args) {
//method1();
//method2();
try {
MyThread10 myThread10 = new MyThread10();
myThread10.start();
// 中的线程
myThread10.interrupt();
// 查看线程是否终止
System.out.println("线程是否停止1 : " + myThread10.isInterrupted());
System.out.println("线程是否停止2 : " + myThread10.isInterrupted());
} catch (Exception e) {
System.out.println(“main catch”);
e.printStackTrace();
}
System.out.println(“end”);
}}
```
结果 :
在这里插入图片描述
可以看出两次都是true,说明 不会清除中断状态.

1.7.3 能停止的线程-异常法

在for循环中判断是否中断:

class MyThread10 extends Thread{
    @Override
    public void run() {
        super.run();
        for (int i = 0; i < 1000000; i++) {
            if (this.isInterrupted()){
                System.out.println("已经中断了...");
                break;
            }
            System.out.println("i=" + (i+1));
        }
        System.out.println("thread run end");
    }
}
public static void main(String[] args) throws InterruptedException {

    try {
        MyThread10 myThread10 = new MyThread10();
        myThread10.start();
        Thread.sleep(1000);
        myThread10.interrupt();
    } catch (InterruptedException e) {
        System.out.println("main catch");
        e.printStackTrace();
    }
    System.out.println("end");
}

结果 :
在这里插入图片描述
虽然线程停止了,但是for循环为的语句不能停止,所以,换一种能够停止所有的方法,例如: 使用异常

class MyThread10 extends Thread{
    @Override
    public void run() {
        super.run();
        try {
            for (int i = 0; i < 1000000; i++) {
                if (this.isInterrupted()){
                    System.out.println("已经中断了...");
                    throw new InterruptedException();
                }
                System.out.println("i=" + (i+1));
            }
System.out.println("thread run end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
    }
}

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

结果 :
在这里插入图片描述

从结果中可以看出,使用异常后如果抛出异常,就不会执行后面的方法.

1.7.4 在沉睡中停止

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

class MyThread10_2 extends Thread{
    @Override
    public void run() {
        super.run();
        try {
            System.out.println("thread begin : ");
            Thread.sleep(200000);
            System.out.println("thread end : ");
        } catch (InterruptedException e) {
            System.out.println("thread catch...");
            e.printStackTrace();
        }
    }
}

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

结果 :
在这里插入图片描述

1.7.5 能停止的线程-暴力停止

使用stop()方法是非常暴力的.

public class Test11 {
    public static void main(String[] args) {
        try {
            MyThread11 myThread11 = new MyThread11();
            myThread11.start();
            Thread.sleep(1000);
            // 使用线程终止
            myThread11.stop();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

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

结果 :
在这里插入图片描述
结果显示: 使用stop()可以实现终止线程的方式,但是实际可能会出现不可预期的结果.
因为强制使用stop()使线程终止,可能会使一些清理性的工作得不到完成,另外,就是对锁定的对象进行了"解锁".导致数据得不到同步处理,出现数据不一致的情况.

1.7.6 方法stop()与java.lang.ThreadDeath异常

调用stop() 方法时会抛出java.lang.ThreadDeath异常,但是通常情况下,这个异常不需要显示的捕捉,.

public class Test12 {
    public static void main(String[] args) {
        MyThread12 myThread12 = new MyThread12();
        myThread12.start();
    }
}
class MyThread12 extends Thread{
    @Override
    public void run() {
        super.run();
        try {
            System.out.println("begin ... ");
            this.stop();
            System.out.println("end ... ");
        } catch (ThreadDeath e) {
            System.out.println("thread catch ...");
            e.printStackTrace();
        }
    }
}

结果:
在这里插入图片描述
注意: 如果抛出异常是exception,则不会显示上述异常:
在这里插入图片描述
结果:
在这里插入图片描述

1.7.7 释放锁的不良后果

使用stop()方法释放锁将会给数据造成不一致的结果.如果出现这种情况,程序处理的数据就与可能造成破坏,最终导致程序执行的流程错误,一定要特别注意.

public class Test13 {
    public static void main(String[] args) {
        try {
            TestUser testUser = new TestUser();
            MyThread13 myThread13 = new MyThread13(testUser);
            myThread13.start();
            Thread.sleep(50);
            // 线程终止
            myThread13.stop();
            System.out.println(testUser.getUsername() + "----" + testUser.getPassword());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class MyThread13 extends Thread {
    private TestUser testUser;
    public MyThread13(TestUser testUser) {
        super();
        this.testUser = testUser;
    }
    @Override
    public void run() {
        super.run();
        testUser.printUser("cc", "bb");
    }
}
class TestUser {
    private String username = "a";
    private String password = "b";

    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public synchronized void printUser(String username, String password) {
        try {
            this.username = username;
            Thread.sleep(1000);
            this.password = password;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

结果 :
在这里插入图片描述
从上可以看出,结果与期望值有区别,是由于数据未同步造成的.在线程调用stop()方法的时候,可能同步方法内的数据还没有执行完,所以数据会有差距.

1.7.8 使用return停止线程

将interrupt() 与 return结合使用也能实现停止线程的效果.

public class Test14 {
    public static void main(String[] args) {
        try {
            MyThread14 myThread14 = new MyThread14();
            myThread14.start();
            Thread.sleep(2000);
            // 线程中断
            myThread14.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class MyThread14 extends Thread {
    private int i = 0;
    @Override
    public void run() {
        super.run();
        while (true) {
            System.out.println("i = " + (i++));
            if (this.isInterrupted()) {
                System.out.println("线程停止了 ... ");
                return;
            }
        }
    }
}

结果 :
在这里插入图片描述
从结果可以看出,达到了结束线程的结果

1.8 暂停线程

暂停线程意味着此线程还可以恢复运行,在java多线程中,可以使用suspend()方法暂时停止线程,使用resume()方法恢复线程.

1.8.1 suspend() 与 resume() 方法的使用
public class Test15 {
    public static void main(String[] args) {
        try {
            MyThread15 myThread15 = new MyThread15();
            myThread15.start();
            Thread.sleep(1000);
            // a 段
            myThread15.suspend();
            System.out.println("a = " + System.currentTimeMillis() + "  id = " + myThread15.getI());
            Thread.sleep(5000);
            System.out.println("a = " + System.currentTimeMillis() + "  id = " + myThread15.getI());
            // b 段
            myThread15.resume();
            System.out.println("b = " + System.currentTimeMillis() + "  id = " + myThread15.getI());
            Thread.sleep(5000);
            System.out.println("b = " + System.currentTimeMillis() + "  id = " + myThread15.getI());
            // c 段
            myThread15.suspend();
            System.out.println("c = " + System.currentTimeMillis() + "  id = " + myThread15.getI());
            Thread.sleep(5000);
            System.out.println("c = " + System.currentTimeMillis() + "  id = " + myThread15.getI());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class MyThread15 extends Thread {
    private long i = 0;
    public long getI() {
        return i;
    }
    public void setI(long i) {
        this.i = i;
    }
    @Override
    public void run() {
        super.run();
        while (true) {
            i++;
        }
    }
}

结果 :
在这里插入图片描述
从结果看,线程确是被暂停了,也被恢复了

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

在使用suspend()与resume()方法时,如果使用不当,极易造成公共的同步对象的独占,使得其他线程无法访问公共同步对象.

public class Test16 {
    public static void main(String[] args) {
        try {
            final Synchr synchr = new Synchr();
            Thread thread = new Thread(() -> {
                synchr.print();
            });
            thread.setName("a");
            thread.start();
            Thread.sleep(1000);
            Thread thread1 = new Thread(() -> {
                System.out.println("线程2启动了 ... 但进入不了 synchr中 ...");
                System.out.println("因为print方法已经被a线程锁定了,并且永远suspend 暂停了 ...");
                synchr.print();
            });
            thread1.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class Synchr {
    public synchronized void print() {
        System.out.println("begin ... ");
        if (Thread.currentThread().getName().equals("a")) {
            System.out.println("线程a 永远 suspend 了");
            // 暂停线程
            Thread.currentThread().suspend();
        }
    }
}

结果 :
在这里插入图片描述

另一种方式独占锁的情况也需要格外注意,稍有不慎,就会掉进"坑"里.

public class Test17 {
    public static void main(String[] args) {
        try {
            MyThread17 myThread17 = new MyThread17();
            myThread17.start();
            Thread.sleep(1000);
            // 暂停线程
            myThread17.suspend();
            System.out.println("main end ");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class MyThread17 extends Thread {
    private long i = 0;
    @Override
    public void run() {
        super.run();
        while (true) {
            i++;
        }
    }
}

结果 :
在这里插入图片描述

使用print方法打印结果:

public class Test17 {
    public static void main(String[] args) {
        try {
            MyThread17 myThread17 = new MyThread17();
            myThread17.start();
            Thread.sleep(1000);
            // 暂停线程
            myThread17.suspend();
            System.out.println("main end ");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class MyThread17 extends Thread {
    private long i = 0;
    @Override
    public void run() {
        super.run();
        while (true) {
            i++;
            System.out.println(i);
        }
    }
}

结果 :
在这里插入图片描述
虽然print方法内部使用synchronized,当程序运行到println()方法内部停止时,同步锁未被释放;
在这里插入图片描述
这导致当前printStream对象的println()方法一直呈"暂停"状态,并且"锁未释放",而main()方法中的代码System.out.println(“main end”); 迟迟不能执行打印.

1.8.3 suspend() 与 resume() 方法的缺点-不同步

使用suspend() 与 resume() 方法时也容易出现因为线程的暂停而导致数据的不同步的情况.

public class Test18 {
    public static void main(String[] args) {
        try {
            MyObject myObject = new MyObject();
            Thread thread = new Thread(() -> {
                myObject.setValue("22", "22");
            });
            thread.setName("a");
            thread.start();
            Thread.sleep(1000);
            Thread thread1 = new Thread(()->{
                myObject.printUsernamePassword();
            });
            thread1.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class MyObject {
    private String username = "1";
    private String password = "11";
    public void setValue(String username, String password) {
        this.username = username;
        if (Thread.currentThread().getName().equals("a")) {
            System.out.println("停止a线程 .. ");
            Thread.currentThread().suspend();
        }
        this.password = password;
    }
    public void printUsernamePassword() {
        System.out.println(username + "---" + password);
    }
}

结果 :
在这里插入图片描述
所以, 使用suspend() 方法要格外注意.

1.9 yield()方法

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

public class Test19 {
    public static void main(String[] args) {
        MyThread19 myThread19 = new MyThread19();
        myThread19.start();
    }
}
class MyThread19 extends Thread{
    @Override
    public void run() {
        super.run();
        long l = System.currentTimeMillis();
        int count = 0;
        for (int i = 0; i < 50000000; i++) {
            Thread.yield();
            count = count + (i + 1);
        }
        System.out.println("费时 : " + (System.currentTimeMillis() - l));
    }
}

结果 :
在这里插入图片描述

1.10 线程的优先级

在操作系统中,线程可以优先划分优先级,优先级较高额线程得到的cpu资源较多,也就是cpu优先执行优先级较高的线程对象中的任务.
设置线程优先级有助于帮助"线程规划器"确定下一次选择那个线程来优先执行,
设置线程的优先级使用setPriority(),源码如下:
在这里插入图片描述
在java中,线程的优先级分为1~10这10个等级,如果小于1或者大于10,则jdk抛出异常throw new IllegalArgumentException();
JDK中线程常用的等级:
在这里插入图片描述

1.10.1 线程优先级的继承特性

在JAVA中,线程的优先级具有继承性,比如,a线程启动b线程,则b线程的优先级与a是一样的.

public class Test20 {
    public static void main(String[] args) {
        System.out.println("main 优先级 start : " + Thread.currentThread().getPriority());
        Thread.currentThread().setPriority(6);
        System.out.println("main 优先级 end : " + Thread.currentThread().getPriority());
        MyThread20 myThread20 = new MyThread20();
        myThread20.start();
    }
}
class MyThread20 extends Thread{
    @Override
    public void run() {
        super.run();
        System.out.println("MyThread20 线程的优先级是 : " + this.getPriority());
        MyThread20_1 myThread20_1 = new MyThread20_1();
        myThread20_1.start();
    }
}
class MyThread20_1 extends Thread{
    @Override
    public void run() {
        super.run();
        System.out.println("MyThread20_1 线程的优先级是 : " + this.getPriority());
    }
}

结果 :
在这里插入图片描述
由图中的结果可以看出,线程的优先级具有继承的特性.

1.10.2 优先级具有规则性

优先级高的线程总是大部分先执行,但不代表高优先级的线程全部先执行完.
当线程优先级的等级差距很大时,谁先执行完和代码的调用顺序无关.
cpu尽量将执行资源让给优先级比较高的线程.

public class Test21 {
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            MyThread21_1 myThread21_1 = new MyThread21_1();
            myThread21_1.setPriority(1);
            myThread21_1.start();

            MyThread21_2 myThread21_2 = new MyThread21_2();
            myThread21_2.setPriority(10);
            myThread21_2.start();
            
        }
    }
}
class MyThread21_1 extends Thread{
    @Override
    public void run() {
        super.run();
        long l = System.currentTimeMillis();
        long addresult = 0;
        for (int i = 0; i < 10; i++) {
            for (int j = 0; j < 5000; j++) {
                Random random = new Random();
                random.nextInt();
                addresult = addresult + 1;
            }
        }
        System.out.println("★ ★ ★ ★ ★ 当前线程用时 : " + (System.currentTimeMillis() - l));
    }
}
class MyThread21_2 extends Thread{
    @Override
    public void run() {
        super.run();
        long l = System.currentTimeMillis();
        long addresult = 0;
        for (int i = 0; i < 10; i++) {
            for (int j = 0; j < 5000; j++) {
                Random random = new Random();
                random.nextInt();
                addresult = addresult + 1;
            }
        }
        System.out.println("☆ ☆ ☆ ☆ ☆ 当前线程用时 : " + (System.currentTimeMillis() - l));
    }
}

结果 :
在这里插入图片描述

public class Test21 {
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            MyThread21_1 myThread21_1 = new MyThread21_1();
            myThread21_1.setPriority(10);
            myThread21_1.start();

            MyThread21_2 myThread21_2 = new MyThread21_2();
            myThread21_2.setPriority(1);
            myThread21_2.start();
            
        }
    }
}

结果 :
在这里插入图片描述

1.10.3 优先级具有随机性

因为线程的优先级还具有"随机性",也就是优先级较高的线程不一定每一次都执行完.
线程的优先级设置相近
结论:
不要把线程的优先级与运行结果的顺序作为衡量的标准,优先级高的线程不一定先执行完run()方法中的任务.

1.10.4 看谁运行的快
public class Test22 {
    public static void main(String[] args) {
        try {
            MyThread22_1 myThread22_1 = new MyThread22_1();
            myThread22_1.setPriority(Thread.NORM_PRIORITY - 3);
            myThread22_1.start();
            MyThread22_2 myThread22_2 = new MyThread22_2();
            myThread22_2.setPriority(Thread.NORM_PRIORITY + 3);
            myThread22_2.start();
            // 当前线程是 主 线程的
            Thread.sleep(1000);
            myThread22_1.stop();
            myThread22_2.stop();
            System.out.println("线程1 : " + myThread22_1.getCount());
            System.out.println("线程2 : " + myThread22_2.getCount());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
class MyThread22_1 extends Thread{
    private int count = 0;
    public int getCount() {
        return count;
    }
    @Override
    public void run() {
        super.run();
        while (true){
            count++;
        }
    }
}
class MyThread22_2 extends Thread{
    private int count = 0;
    public int getCount() {
        return count;
    }
    @Override
    public void run() {
        super.run();
        while (true){
            count++;
        }
    }
}

结果 :
在这里插入图片描述

1.11 守护线程

java 线程中有两种线程,一种是用户线程,另一种是守护线程
守护线程是一种特殊的线程,他的特性是 “陪伴”,当进程中不存在非守护线程了,那守护线程就自动销毁(随着jvm一同结束工作).
典型的守护线程就是垃圾回收线程

class MyThread4 extends Thread{
    private int i = 0;
    @Override
    public void run() {
        while (true){
            i++;
            System.out.println("当前i的值是 : " + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
/**
 * 关于守护线程
 *  java 中线程有两种, 一种是用户线程,一种是守护线程
 *  我们通过new thread().start() 的线程是用户线程
 *  守护线程 是通过用户线程的 setDaemon()方法设置的,
 *  守护线程可以理解为未用户线程服务的,当jvm中没有非守护线程后,守护线程会随着jvm一同结束工作
 */
private static void aboutThreadDaemon() throws InterruptedException {
    MyThread4 myThread4 = new MyThread4();
    // 设置为守护线程
    myThread4.setDaemon(true);
    myThread4.start();
    System.out.println("当前线程 : " + Thread.currentThread().getName());
    System.out.println("当前线程状态 : " + Thread.currentThread().getState());
    System.out.println("当前线程是否存活 : " + Thread.currentThread().isAlive());
    Thread.sleep(2000);
    System.out.println("当前线程 : " + Thread.currentThread().getName());
    System.out.println("当前线程状态 : " + Thread.currentThread().getState());
    System.out.println("当前线程是否存活 : " + Thread.currentThread().isAlive());
    System.out.println("当前线程是否存活 : " + Thread.currentThread().isAlive());
    System.out.println("我离开,myThread4对象也不在打印了,结束了");
}

结果 :
在这里插入图片描述

1.12 本章小结

本章介绍了thread类的api,在使用这些api’的过程中,会出现一些意想不到的情况,其实也就是多线程具有不可预知性的一个体现,学习掌握这些常用情况,也就掌握了多线程开发的命脉与习性,是学习多线程更深层知识的基础.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值