Java中的线程

1、线程

1.1 什么是线程

现代操作系统在运行一个程序的时候会开启一个进程。调度的最小单位是线程,一个进程里会有多个线程。这多个线程共享进程资源。

进程是CPU调度资源的最小单位,线程是CPU调度和程序执行的最小单位。

进程有自己的独立地址空间,而线程只有自己的堆栈空间和局部变量。

进程切换需要很大的开销,线程切换开销比较小。

1.2 Java天生是多线程的

在main方法启动时,会启动一个main线程,同时还有垃圾回收器的线程、对象销毁时执行对象的finaize()的线程finalizer。所以Java天生就是多线程的。

1.3 线程调度

线程调度是指系统为线程分配处理器使用权的过程。

线程调度有以下2种方式:

  1. 协同式调度: 线程的执行时间由线程本身来控制,线程把自己的工作执行完了,主动通知系统切换到另一个线程上。好处是操作简单,线程干完自己的事情才会进行线程切换。坏处是线程执行时间不可控制,如果一个线程编写有问题,一直不告知系统进行线程切换,程序就会一直阻塞在那里。
  2. 抢占式调度: 每个线程由系统来分配时间,线程的切换不由线程自己本身来决定。在这种调度方式下,线程的执行时间是系统可控的,不会出现一个线程 导致整个进程阻塞的问题。
    可以通过设置线程优先级来给某些线程多分配一点执行时间,另外的一些线程可以上分配一点。一共有10个级别的线程优先级。

1.4 线程的状态和转换

  1. 新建:创建后尚未启动的线程处于这种状态
  2. 运行/就绪:处于此状态的线程有可能正在执行,也有可能正在等待着CPU为它分配资源
  3. 无限期等待:处于这种状态的线程不会被CPU分配执行时间,他们要等待被其他线程显示的唤醒。以下方法会让线程陷入无限期的等待状态:
    • 没有设置Timeout参数的Object.wait()方法
    • 没有设置Timeout参数的Thread.join()方法
  4. 限期等待: 处于这种状态的线程也不会被分配CPU执行时间。但是无需等待被其他线程显示的唤醒,在一定时间之后它们会由系统自动唤醒。
    • Thread.sleep()方法
    • 设置了Timeout参数的Object.wait()方法
    • 设置了Timeout参数的Thread.join()方法
  5. 阻塞: 线程被阻塞了。阻塞和等待的区别是:阻塞是在等待着获取一个排他锁,这个事件将在另一个线程放弃这个锁的时候发生;而等待状态则是在等待一段时间,或者唤醒动作的发生。
  6. 结束:已终止线程的线程状态,线程已经结束执行。

线程interrupt不会中断一个正在运行的线程,它实际的方法是给正在阻塞的线程发送一个interrupt信号,会抛出interruptException,可以在代码里捕捉到这个异常来退出阻塞状态。

1.5 死锁

死锁产生的四个条件:

  • 互斥条件:一个资源每次只能被一个线程使用
  • 一个线程因请求资源而阻塞时,对已获得的资源保持不放
  • 线程已获得的资源,在未使用完之前,不能强行剥夺
  • 若干线程之间形成一种头尾相连接的循环等待资源关系

上述四个条件都满足才会发生死锁。
最常见的死锁就是循环使用synchronized。也就是第四种,破坏第四种就不会发生死锁。

public class DeadLock {

    private static String objectA = "A";

    private static String objectB = "B";

    static class MyThead implements Runnable {

        @Override
        public void run() {
            synchronized (objectA) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (objectB) {
                    System.out.println("1");
                }
            }
        }
    }

    static class MyThread2 implements Runnable {

        @Override
        public void run() {
            synchronized (objectB) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (objectA) {
                    System.out.println("2");
                }
            }
        }
    }

    public static void main(String[] args) {
        MyThead myThead = new MyThead();
        Thread thread1 = new Thread(myThead);
        thread1.start();

        MyThread2 myThread2 = new MyThread2();
        Thread thread2 = new Thread(myThread2);
        thread2.start();
    }

}

1.6 守护线程

​ Demon线程是一种程序后台调度和支持工作,当JVM不存在Demon线程时,JVM将会退出。使用Thread.setDemon(true)设置守护线程。

Demon线程需要在调用start()方法之前设置

​ Demon线程在Java虚拟机退出后,finally里面的代码不一定会执行。在main方法执行完后,虚拟机将会退出,所有守护线程被终止。

在构建守护线程时,不能依靠finally代码块的内容来确保执行关闭或清理资源的逻辑。

1.7 线程中断和安全停止

1.7.1 中断

中断是线程的一个中断标识位,其他线程通过调用某个线程的interrupt()方法,将这个线程的中断标志为设置为true。

​ 线程可以通过isInterrupted()方法来检测自身是否被中断,也可以调用Thread.interrupted()方法重置中断标识位。

​ 许多抛出InterruptedException方法(Thread.sleep()),在异常抛出之前,就会将中断标识位清除(false),此时调用Thread.interrupted()会返回false.

​ 这些方法抛出InterruptedException,是为了别的方法中断此线程时,可以在catch语句块里面写响应中断的方法。

1.7.2 安全停止

安全地停止线程, 通过线程响应别的线程的中断事件,可以让当前运行线程释放资源,这样中断线程比较优雅。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值