欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java多线程基本技能

程序员文章站 2022-05-06 08:01:19
...

参考:《Java多线程编程核心技术》

一、程序、线程与进程

  • 程序:程序是存储在磁盘上, 包含可执行机器指令和数据的静态实体
  • 进程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J0Txw49h-1604215640129)(C:\Users\VSUS\Desktop\笔记\多线程\img\1.png)]

上面是百度百科对进程这个词条的解释,程序是静态的,存储在磁盘上,进程是动态的,当程序运行起来就成了进程。

  • 线程:在百度百科对进程的解释中,已经说到了进程是线程的容器。线程是在进程中独立运行的子任务,这也说明了进程与线程是包含的关系

二、创建线程的两种方法

在Java的jdk中已经自带了对多线程技术的支持,实现多线程的方式主要有两种:

  • 继承Thread类
  • 实现Runnable接口
1. 继承Thread类
public class MyThreadTest {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
        //thread.start();
        System.out.println("执行结束");
    }
}
class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("MyThread!!");
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vd7NwY5k-1604215640133)(C:\Users\VSUS\Desktop\笔记\多线程\img\2.png)]

创建一个类继承Thread,然后重写里面的run方法,在main方法中,通过Thread对象的start()方法去启动一个线程,通过执行结果,我们已经可以看到CPU是先执行MyThread类总的run方法的,可见多线程的执行顺序与代码的顺序没有关系!

2. 实现Runnable接口

java的单继承机制就决定了如果只有继承Thread的方法是具有局限性的,所以出现了实现Runnable接口的方法,同样是上面的代码思路

public class MyThreadTest {
    public static void main(String[] args) {

        Thread t1 = new Thread(new MyThread());
        t1.start();
        System.out.println("执行结束");
    }
}
class MyThread implements Runnable {

    @Override
    public void run() {
        System.out.println("MyThread!!");
    }
}

需要启动用实现Runnable方法的线程,可以将其作为参数传入Thread类中,下面是Thread类的构造方法

Java多线程基本技能

可见,Thread类的构造方法可以传入一个Runnable接口的实例,多线程的启动永远都是Thread类的start()方法

也可以使用匿名内部类、lamdba的方式去简洁的创建线程

public class MyThreadTest {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("MyThread!!");
            }
        });
        t1.start();
        System.out.println("执行结束");
    }
}
    public static void main(String[] args) {
        Runnable runnable = () -> System.out.println("MyThread!!");
        Thread t1 = new Thread(runnable);
        t1.start();
        System.out.println("执行结束");
    }
3.一些注意
  1. 要启动多线程,必须调用start方法,而不是run方法,调用run方法只是在主线程里顺序的执行
  2. 执行start方法的顺序,并不代表线程的先后执行顺序
public class MyThread extends Thread {
    private int i;
    public MyThread(int i){
        this.i = i;
    }

    @Override
    public void run() {
        System.out.println(i);
    }
}
public class Run {
    public static void main(String[] args) {
        MyThread t1 = new MyThread(1);
        MyThread t2 = new MyThread(2);
        MyThread t3 = new MyThread(3);
        MyThread t4 = new MyThread(4);
        MyThread t5 = new MyThread(5);
        MyThread t6 = new MyThread(6);
        MyThread t7 = new MyThread(7);
        MyThread t8 = new MyThread(8);
        MyThread t9 = new MyThread(9);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        t6.start();
        t7.start();
        t8.start();
        t9.start();
    }
}

执行后的顺序为

Java多线程基本技能

可见,多线程的执行顺序根调用顺序并无联系!

  1. 不能多次调用start方法
public class MyThreadTest {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
        thread.start();
        System.out.println("执行结束");
    }
}
class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("MyThread!!");
    }
}

多次调用start()方法,会抛出IllegalThreadStateException异常,

Java多线程基本技能

  1. Thread类也实现了Runnable接口

Java多线程基本技能

这就意味着构造函数Thread(Runnable target)不光可以传入Runnable接口的对象,还可以传入一个Thread的对象,这样可以将Thread对象中的run()方法交给其他线程调用,由 其他线程启动线程

三、多线程的常用方法

1.线程的命名与取得
  1. currentThread()

Java多线程基本技能

  1. setName()

    ​ 给线程设置名字

  2. getName()

    ​ 获取线程的名字

public class CountOperate extends Thread {
    public CountOperate() {
        System.out.println("CountOperate---begin");
        System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName());
        System.out.println("this.getName()=" + this.getName());
        System.out.println("CountOperate---end");
    }

    @Override
    public void run() {
        System.out.println("run---begin");
        System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName());
        System.out.println("this.getName()=" + this.getName());
        System.out.println("run---end");

    }
}
public class Run {
    public static void main(String[] args) {
        CountOperate countOperate = new CountOperate();
        Thread t1 = new Thread(countOperate);
        t1.setName("A");	//设置线程名字
        t1.start();			//启动线程
    }
}

运行结果:

Java多线程基本技能

上面的代码还可以证明一个结论:CountOperate的构造方法只在main线程里执行的!

2. 线程休眠(sleep方法)

sleep方法的作用是在指定的毫秒数内让当前“正在执行的线程”休眠一段时间,注意:如果调用sleep的线程上拥有该对象的锁,那么调用sleep不会释放锁

public class MyThread extends Thread{
    @Override
    public void run() {
        try {
            System.out.println("run threadName =" + this.currentThread().getName() + " begin =" + System.currentTimeMillis());
            Thread.sleep(2000);
            System.out.println("run threadName =" + this.currentThread().getName() + " end =" + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class Run {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        System.out.println("run threadName =" + Thread.currentThread().getName() + " begin =" + System.currentTimeMillis());
        thread.start();
        System.out.println("run threadName =" + Thread.currentThread().getName() + " end =" + System.currentTimeMillis());
    }
}

Java多线程基本技能

3.线程让步yield()方法

调用yiled方法,会让线程交出CPU方法,让CPU去执行其他的线程,更sleep方法类似,也不会释放锁,但是yield不能控制具体的交出CPU的时间,注意:调用yield方法并不会让线程进入堵塞状态,而是让线程重回就绪状态,也许当前线程刚调用yiled方法,交出了CPU的控制权,而下次CPU又选择了该线程执行

public class MyThread implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            Thread.yield();
            System.out.println("run threadName =" + Thread.currentThread().getName() + " i=" + i);
        }
    }
}
public class Run {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        new Thread(t1).start();
        new Thread(t1).start();
        new Thread(t1).start();
    }
}

执行结果:

Java多线程基本技能

4.线程停止方法

在Java中,有3种方法可以终止正在运行的线程

  • 使用退出标志,是线程正常退出,也就是当run方法完成后线程终止
  • 使用stop方法强行停止线程,但是这个方法以及被抛弃,该方法不安全
  • 使用interrupt方法
  1. 使用退出标志
public class MyThread implements Runnable{
    private boolean flag;

    @Override
    public void run() {
        try {
            flag = true;
            int i  = 1;
            while(flag) {
                    Thread.sleep(1000);
                    System.out.println("这是第" + i + "运行线程"+ "线程名称为" + Thread.currentThread().getName());
                    i++;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void setFlag(boolean flag){
        this.flag = flag;
    }
}
public class Run {
    public static void main(String[] args) {

        try {
            MyThread t1 = new MyThread();
            new Thread(t1).start();
            Thread.sleep(2000);
            t1.setFlag(false);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Java多线程基本技能

  1. 使用interrupt

使用interrupt并不会马上就停止操作,调用interrupt()方法只是在当前线程上中打了一个停止的标记,和上面例子差不多,只不过上面的例子是手动打上这个标记,而调用interrupt()后JVM帮我们把这个标记打上。那么就需要有一个可以判断当前线程标记是否停止的方法,有两种方法可以判断当前线程是否停止

  • this.isinterrupt:测试当前线程是否已经中断,执行后具有将状态标志置清除为false
  • this.interrupted:测试线程是否已经中断,不清除状态标志

仅仅使用interrupt()方法只是在当前线程打了一个停止的标记,并不是真的停止线程:

public class MyThread implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 500000; i++) {
            System.out.println("i= " + (i + 1));
        }
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        Thread t1 = new Thread(myThread);
        t1.start();
        Thread.sleep(2000);
        t1.interrupt();
    }
}

Java多线程基本技能

  1. 使用异常或者return来停止线程
public class MyThread implements Runnable{
    @Override
    public void run() {
        try {
            for (int i = 0; i < 500000; i++) {
                if(Thread.interrupted()){
                    System.out.println("停止状态!!");
                    throw new InterruptedException();
                }
                System.out.println("i= " + (i + 1));
            }
            System.out.println("我在for循环下面");
        }catch (InterruptedException e){
            System.out.println("进入catch!");
        }

    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        Thread t1 = new Thread(myThread);
        t1.start();
        Thread.sleep(2000);
        t1.interrupt();
    }
}

Java多线程基本技能

5. 线程的优先级
  • setPriority

设置线程的优先级

  • getPriority

取得线程的优先级

线程的优先级用整形数字表示,一共有1~10这10个等级,jdk中有3个常量来预置定义的优先级,默认优先级是5

 /**
     * The minimum priority that a thread can have.
     */
    public final static int MIN_PRIORITY = 1;

   /**
     * The default priority that is assigned to a thread.
     */
    public final static int NORM_PRIORITY = 5;

    /**
     * The maximum priority that a thread can have.
     */
    public final static int MAX_PRIORITY = 10;
  1. 优先级具有继承性

例如A线程启动B线程,则B线程的优先级和A一样

//线程1
public class MyThread1 implements Runnable {
    @Override
    public void run() {
        System.out.println("MyThread1 run priority = " + Thread.currentThread().getPriority());
        new Thread(new MyThread2()).start();
    }
}
//线程2
public class MyThread2 implements Runnable {
    @Override
    public void run() {
        System.out.println("MyThread2 run priority = " + Thread.currentThread().getPriority());

    }
}
//主线程
public class Run {
    public static void main(String[] args) {
        System.out.println("main thread begin priority= " + Thread.currentThread().getPriority());
        Thread.currentThread().setPriority(6);
        System.out.println("main thread end priority= " + Thread.currentThread().getPriority());
        new Thread(new MyThread1()).start();
    }
}

这里说的继承并不是说继承的那种关系,而是指如果A线程启动B线程,那么A,B线程的优先级,默认就一样,上面代码中,main线程启动MyThread1线程,MyThread1启动MyThread2线程,如果改变了main线程的优先级那么MyThread1,MyThread2的优先级都会改变

Java多线程基本技能

相关标签: Java