Java多线程 - 线程实现,线程状态(生命周期)

原文地址:http://chenxiaoqiong.com/articles/thread1/

为什么要用多线程

使用多线程只有一个目的,那就是更好的利用cpu的资源。通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源。

在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统内多个程序间并发执行的程度。

线程的实现

Java 提供了三种创建线程的方法

  • 通过继承 Thread 类本身;
  • 通过实现 Runnable 接口;
  • 通过 Callable 和 Future 创建线程。

继承 Thread 类

将一个类声明为Thread的子类。 这个子类应该重写Thread类的run方法 。 例如:

 public class MyThread extends Thread {
    @Override
    public void run() {
        // 这里写线程需要完成的任务
        System.out.println(Thread.currentThread().getId());
    }
}

其中run()方法的方法体代表了线程需要完成的任务,称之为线程执行体。当创建此线程类对象时一个新的线程得以创建,并进入到线程新建状态。以下代码将创建一个线程并启动它运行:

    Thread t1 = new MyThread();
    t1.start();

通过调用线程对象引用的start()方法启动线程。start()方法使得该线程进入到就绪状态,此时此线程并不一定会马上得以执行,这取决于CPU调度时机。
注意: 不是调用run()方法启动线程,run方法中只是定义需要执行的任务,如果调用run方法,即相当于在主线程中执行run方法,跟普通的方法调用没有任何区别,此时并不会创建一个新的线程来执行定义的任务。

实现 Runnable 接口

创建一个类声明实现Runnable接口,然后这个类必须实现run方法。 在创建Thread时作为参数传递,并启动。例如:

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        // 这里写线程需要完成的任务
        System.out.println(Thread.currentThread().getId());
    }
}

以下代码将创建一个线程并启动它运行:

    MyRunnable myRunnable =new MyRunnable();
    Thread t2 = new Thread(myRunnable);
    t2.start();

注意: 事实上,查看Thread类的实现源代码会发现Thread类是实现了Runnable接口的。在Java中,这2种方式都可以用来创建线程去执行子任务,具体选择哪一种方式要看自己的需求。直接继承Thread类的话,可能比实现Runnable接口看起来更加简洁,但是由于Java只允许单继承,所以如果自定义类需要继承其他类,则只能选择实现Runnable接口。

通过 Callable 和 Future 创建线程(实现有返回结果的多线程)

创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。

class MyCallable implements Callable<T> {
    ...
    public T call() throws Exception {
        // 这里写线程需要完成的任务

    }
}

创建 Callable 实现类的实例,两种方式启动线程并获得返回值:Java多线程 -(Callable 和 Future实现有返回值的线程)

线程状态(生命周期)

线程是一个动态执行的过程,它也有一个从产生到死亡的过程。下图显示了一个线程完整的生命周期:

新建状态(New): 当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();

就绪状态(Runnable): 当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,等待JVM里线程调度器的调度。

运行状态(Running): 就绪状态的线程获取了CPU使用权,执行程序代码。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

阻塞状态(Blocked): 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

  1. 调用join()和sleep()方法。sleep()时间结束或被打断,join()中断,IO完成都会回到Runnable状态,等待JVM的调度。
  2. 调用wait(),使该线程处于等待池(wait blocked pool),直到notify()/notifyAll();线程被唤醒被放到锁定池(lock blocked pool)。释放同步锁使线程回到可运行状态。
  3. 对Running状态的线程加同步锁(Synchronized)使其进入(lock blocked pool )。同步锁被释放进入可运行状态(Runnable)。

死亡(Dead): 一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

概念

进程:进程是一个执行中的程序,一个进程包括由操作系统分配的内存空间,包含一个或多个线程。进程间的切换会有较大的开销。(进程是资源分配的最小单位)

线程:线程是进程的一部分,它不能独立的存在。同一类线程共享进程所拥有的资源,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(线程是cpu调度的最小单位)

多线程:指的是这个程序(一个进程)运行时产生了不止一个线程。(用多线程只有一个目的,那就是更好的利用cpu的资源)

并行与并发:
并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源。

线程安全:线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。
*经常用来描绘一段代码。指在并发的情况之下,该代码经过多线程使用,线程的调度顺序不影响任何结果。这个时候使用多线程,我们只需要关注系统的内存,cpu是不是够用即可。反过来,线程不安全就意味着线程的调度顺序会影响最终结果,如不加事务的转账代码:
void transferMoney(User from, User to, float amount){
to.setMoney(to.getBalance() + amount);
from.setMoney(from.getBalance() - amount);
}*

同步:Java中的同步指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确。如上面的代码简单加入@synchronized关键字。在保证结果准确的同时,提高性能,才是优秀的程序。线程安全的优先级高于性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值