内容纲要

1.进程和多线程的概念及线程的优点

进程:进程是操作系统结构的基础,是一次程序的执行,是一个程序及其数据在处理机上顺序执行时所发生的活动;是程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。
线程:可以理解成是在进程中独立运行的子任务。
多线程的优点:同一时间内运行更多不同种类的任务,提高CPU利用率,是系统效率大大得到提升。
Java:进程和多线程

2.使用多线程

一个进程在运行时至少有一个线程在运行。

2.1 继承Thread类

创建一个线程的方式有两种,一种是继承Thread类,一种是实现Runnable接口。使用Thread类时最大的局限就是不支持多继承,因为Java语言时单继承的。所以为了实现多线继承,完全可以实现Runnable接口,二者没有什么本质的区别。
Thread类结构

public class Thread implements Runnable {

}

Thread类实现了Runnable接口,它们之间具有多态关系。

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

2.2 实现Runnable接口

public class MyRunnable implements Runnable {
    public void run() {
        System.out.println("my thread is running ...");
    }
}

Thread类也支持传入一个Ruunable接口的对象

public class Main {
    public static void main(String[] args) {
        MyRunnable thread = new MyRunnable();
        thread.run();

        Runnable runnable = new MyRunnable();
        Thread thread1 = new Thread(runnable);
        thread1.start();
    }
}

2.3 实例变量与线程安全

自定义线程类中的实例变量针对其他线程可以有共享和不共享之分,这在多线程之间进行交互时是一个很重要的技术点。
(1)不共享数据的情况

public class MyThread extends Thread {
    private int count = 5;
    public MyThread (String name){
        super();
        this.setName(name);
    }

    @Override
    public void run() {
        super.run();
        while (count > 0){
            count--;
            System.out.println("由"+ this.currentThread().getName() + "计算, count=" + count);
        }
    }
}
public class Main {
    public static void main(String[] args) {
        MyThread thread = new MyThread("A");
        MyThread thread1 = new MyThread("B");
        MyThread thread2 = new MyThread("C");
        thread.start();
        thread1.start();
        thread2.start();
    }
}

每个线程有各自的count变量,自己减少自己的count变量。这就是变量不共享。
(2)共享数据的情况

public class Section4Thread extends Thread {
    private int count = 5;

    @Override
    public void run() {
        super.run();
        System.out.println("由" + Thread.currentThread().getName() + "计算count:" + count);
        count--;
    }
}
public class Run {
    public static void main(String[] args) {
        Section4Thread thread = new Section4Thread();
        Thread a = new Thread(thread, "A");
        Thread b = new Thread(thread, "B");
        Thread c = new Thread(thread, "C");
        Thread d = new Thread(thread, "D");
        Thread e = new Thread(thread, "E");
        a.start();
        b.start();
        c.start();
        d.start();
        e.start();

    }
}

也可以用synchronized关键字来同步方法实现。

3. currentThread()方法

currentThread()方法返回当前代码段正在被哪个线程调用的信息。

4. isAlive()方法

isAlive()方法判断当前的线程是否处于活动状态。
Thread.currentThread 和 this的差异

public class Section5Thread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread.currentThread(): " + Thread.currentThread().getName() + Thread.currentThread().isAlive());
        System.out.println("this : "+this.getName() + this.isAlive() );
    }
}
public class Main {
    public static void main(String[] args) {
        Section5Thread t = new Section5Thread();
        Thread thread = new Thread(t);
        thread.setName("AAA");
        thread.start();
    }
}

5. sleep()方法

在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行),这个“正在执行的线程”指的是this.currentThread()返回的线程。
思考:线程的run()方法和start()方法的差别?

6. getId()方法

getId()方法的作用是取得线程的唯一标识。

7. 停止线程

停止一个线程意味着在线程处理完任务之前停掉正在做的操作,也就是放弃当前的操作。 介绍三种方式停止线程
1.使用退出标志,使线程正常退出,也就是当run方法完成后线程停止。
2.使用stop方法强行终止线程,但不推荐,以为stop()和suspend()及resume()一样,都是作废过期的方法,使用它们可能产生不可预料的后果。
3.使用Interrupt方法中断线程

7.1 停止不了的线程

public class MyThread extends Thread {
    @Override
    public void run() {
        super.run();
        for (int i = 0; i < 50000; i++) {
            System.out.println("i = " + (i + 1));
        }
    }
}
public class Main {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        try {
            thread.start();
            Thread.sleep(2000);
            thread.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

执行后,日志确实是5000行,线程并没有中断,也就是说调用interrupt()方法并没有中断线程。那如何停止线程呢?

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

this.interrupted():测试当前线程是否已经中断。—> 测试当前线程是否已经是中断状态,执行后具有将清除状态标志为false的功能。
ths.isInterrupted():测试线程是否已经中断。—> 测试线程Thread对象是否已经是中断状态,但不清楚状态标志。
总结:interrupted()告诉当前线程,执行完之后,就要被中断。isInterrupted()询问线程是否中断了?

7.3 能停止的线程——异常法

靠throw出InterruptException()然后捕获该异常来停止线程。

7.4 沉睡中停止

7.5 stop()暴力停止

直接调用stop()方法。这样会抛出ThreadDeath()异常,通常情况不需要显示捕捉。
stop()方法已经作废,如果强制让线程停止有可能会造成一些请理性工作得不到完成,以及对锁定的对象就行了“解锁”,导致数据得不到同步处理,出现数据不一致的情况。使用stop()方法释放锁将会给数据造成不一致的后果。

7.6 使用return停止线程

public class MyThread extends Thread {
    @Override
    public void run() {
       while (true) {
           if(this.isInterrupted()) {
               System.out.println("thread was stopped !");
               return;
           }
       }
    }
}
public class Main {
    public static void main(String[] args) throws InterruptedException {
        MyThread thread = new MyThread();
        thread.start();
        Thread.sleep(2000);
        thread.interrupt();
    }
}

8. 暂停线程

暂停线程意味着可以恢复运行,使用Thread.suspend()和Thread.resume()方法来是暂停和恢复线程。(注:这两方法都被标记为过期作废的方法,因此不推荐使用)
它们的缺点——独占,使用不当时,极易造成公共的同步对象的独占,使得其他线程无法访问公共的同步对象。

9.yield方法

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

10.线程的优先级

设置线程优先级使用setPriority()方法。在Java中,线程的优先级分为1~10这10个等级。如果大于10或者小于1,则JDK抛出异常throw new IllegalArgumengException()。
setPriority()方法源代码如下

public final void setPriority(int newPriority) {
    ThreadGroup g;
    checkAccess();
    if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY){
        throw new IllegalArgumentException();
    }
    if((g = getThreadGroup()) != null) {
        if(newPriority > g.getMaxPriority()){
            newPriority = g.getMaxPriority();
        }
        setPriority0(priority = newPriority);
    }
}

1.线程优先级的继承特性
A线程启动B线程,则B线程的优先级与A线程的优先级是一致的。
2.线程优先级的具有规则性
高优先级的线程总是大部分先执行完,但不代表高优先级的线程全部先执行完。也就是说线程的优先级具有一定的规则性的,CPU尽量将执行资源分配给优先级高的线程。
3.优先级具有随机性
优先级高的线程并不一定每一次都先执行完run()方法中的任务。

11.守护线程

Thread.setDaemon(true); //设置线程为守护线程。

当线程中存在非守护线程时,守护线程就存在。
概念: 只要当前JVM实例中存在任何一个非守护线程没有结束,守护线程就在工作,只有当最后一个非守护线程结束时,守护线程才随着JVM一同结束工作。它的作用就是为线程提供便利服务。守护线程最典型的应用就是GC(垃圾回收器)。

By liu luli

8年IT行业从业经验,参与、负责过诸多大型项目建设。掌握多门编程语言,对Java、Python编程有较为深刻的理解。现为杭州某公司开发负责人。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注