Java线程和多线程(一)——线程的基本概念
Java 线程是一个轻量级执行任务的处理单元。Java提供了Thread
类来支持多线程,开发者在应用中可以创建多个线程来支持并发执行任务。
在应用中存在两种类型的线程,用户线程和守护线程。当我们启动应用的时候,main函数是第一个启动的用户线程,开发者可以在之后自由的创建用户线程。当所有的线程执行完毕,JVM会终结掉程序。
开发者也可以配置不同线程的优先级,但是这并不代表更高优先级的线程会比低优先级的线程先执行。因为线程的执行取决于线程调度器,而线程调度器的实现是基于操作系统的。当一个线程启动后,它的执行便交给了操作系统的线程调度器,JVM不再拥有其控制的权限。
开发者可以通过实现Runnable
接口,或者是继承Thread
类来实现多线程。
Thread t = new Thread(new Runnable(){
@Override
public void run() {
}
});
上面的代码就是最简单的用来创建线程的方式,其中,我们使用到了Runnable
的匿名类。
Java线程
进程和线程是执行的基本单元。Java的并发编程更为关注的是线程。
进程
进程是一个包含了所有执行环境的单元,可以将其看作为一个程序或者应用。然而程序本身内部也可能包含多个进程。Java的运行环境是作为一个单独的线程来工作的,但其内部包含了不同的类以及程序。
线程
线程也被称作轻量级的进程。线程要求的资源更少,并且存在于进程之中,所有的线程共享进程的资源。
Java多线程
每一个Java的应用都至少包含一个线程——主线程。尽管后台也会存在一些其他的线程,例如内存管理,系统管理,信号处理等等,但是从应用来看,主函数是第一个线程,并且我们可以从其中创建多个线程。
多线程指的是2个或者更多的线程来在一个程序中并发地执行任务。单处理的电脑只能在同一时间执行一个线程,时间分片是操作系统给不同的进程线程用来共享处理器时间的。
线程的优点
- 线程与进程相比更为轻量级,创建线程的资源远远少于进程。
- 线程共享其父进程的数据和代码。
- 线程间的上下文切换的代价比进程间上下文的切换代价小很多。
- 线程之间的交互相对于进程来说更为简单。
Java提供了两种方式来创建线程。
- 实现
java.lang.Runnable
接口 - 继承
java.lang.Thread
类
Thread
VS Runnable
因为Java为单继承模式,实现Runnable
接口更为实用,因为Java支持实现多个接口,但是如果继承了Thread,就无法继承其他的类了。
Java线程休眠
java.lang.Thread
提供了一个sleep()
方法可以用来暂停线程指定的毫秒数。其参数不能为负值,否则会抛出IllegalArgumentException
。
当然,还有sleep(long millis, int nanos)
是sleep()
函数的一个重载函数,这个函数可以指定精确到纳秒级,其中,纳秒参数值在0~999999之间。
下面的例子是调用Thread.sleep()
的一个例子,等待时间为2秒。
public class ThreadSleep {
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
Thread.sleep(2000);
System.out.println("Sleep time in ms = "+(System.currentTimeMillis()-start));
}
}
当然,如果读者运行程序的话,会发现实际运行的时间是多余2000毫秒的,这都是基于操作系统的线程调度器。
线程休眠关键点
sleep()
会暂停当前的线程执行。- 实际的休眠时间取决于操作系统的定时器以及调度器,对于一个空闲的系统来说,休眠的时间很接近实际的执行时间,但是对于一个繁忙的系统来说,休眠时间会多一些。
- 线程休眠不会丢失信息,或者锁定当前线程获得的信息。
- 其他的线程都可以打断当前线程的sleep,那样的话会抛出
InterruptedException
。
线程休眠的原理
Thread.sleep()
是和线程调度器来进行交互实现的,该函数会将当前的线程状态置为等待状态指定的时间,一旦等待的时间结束了,线程的状态会重新变为Runnable状态,等待CPU的执行。所以时间线程休眠的时间是跟操作系统的线程调度器相关的。
Java线程的Join
Java Thread Join:该方法用来暂停其他进程的执行,直到当前的线程死亡。一下有3个重载的方法。
public final void join()
:这个方法会将当前执行的线程置于等待状态,直到调用join
方法的线程的执行结束。如果线程被中断,会抛出InterruptedException
.public final synchronized void join(long millis)
:这个join
方法是用来等待调用的线程死亡或者是指定的毫秒数。因为线程的执行是依赖于操作系统的实现的,这个操作也不能保证配置的时间为当前线程等待的精确时间。public final synchronized void join(long millis, int nanos)
:该join
方法和上面的方法类似,不过可配参数为毫秒和纳秒的组合。
下面的例子展示了Thread.join
方法的一些用法。程序的目标是为了确保主线程是最后一个结束的线程,第三个线程要在第一个线程死亡后才开始执行。
package com.sapphire.threads;
public class ThreadJoinExample {
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable(), "t1");
Thread t2 = new Thread(new MyRunnable(), "t2");
Thread t3 = new Thread(new MyRunnable(), "t3");
t1.start();
//start second thread after waiting for 2 seconds or if it's dead
try {
t1.join(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
//start third thread only when first thread is dead
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
t3.start();
//let all threads finish execution before finishing main thread
try {
t1.join();
t2.join();
t3.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("All threads are dead, exiting main thread");
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("Thread started:::"+Thread.currentThread().getName());
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread ended:::"+Thread.currentThread().getName());
}
}
输出的结果为:
Thread started:::t1
Thread started:::t2
Thread ended:::t1
Thread started:::t3
Thread ended:::t2
Thread ended:::t3
All threads are dead, exiting main thread
上面的例子大体可以看出join
方法的作用,自定义的线程执行时间为4000ms,因为线程1join
了2秒,所以线程1跟2均启动了,然后线程1重新join,造成了线程3是在线程1执行之后才启动的。之后因为线程2和线程3都进行了join
,所以主线程在最后才结束。
线程状态
下图中展示了Java线程中不同的状态,需要注意的是,当开发者可以创建线程并启动,但是线程的状态如何从Runnable
到Running
到Blocked
等状态,是取决于OS中的线程调度算法的,Java本身并不能完全控制。
New
当开发者使用new
操作符来创建一个Thread
的时候,线程本身的状态就是New
的状态。在这个时候,线程还没有开始存活,这个状态属于Java编程范围内的一个内部状态。
Runnable
当开发者调用Thread
的实例方法start()
以后,线程的状态就从New
状态变成了Runnable
,这个时候,线程的控制权就交给了操作系统的线程调度器来继续线程的执行。是立刻执行该线程,还是将其扔到线程池等待运行就取决于操作系统的线程调度算法了。
Running
当线程正在执行时,其状态就变成了Running
状态。线程调度器从线程池中会挑选一个Runnable
状态的线程,将其状态改为Running
然后由CPU开始执行该线程。线程根据时间的分片情况,比如完成线程方法或者等待资源等,可以将其状态改为Runnable
,Dead
或者Blocked
等。
Blocked/Waiting
线程可以处于Waiting
状态直到其他线程调用Thread.join()
或者直到一些资源有效。比如,生产者消费者问题或者是等待唤醒或者是IO资源等,这个时候线程的状态是Waiting
状态。一旦线程的等待状态结束了,其状态会重新变成Runnable
,回到Runnable
的线程池之中。
Dead
一旦线程完成了执行,其状态会变成Dead
并且认为线程死亡。
Java线程和多线程(一)——线程的基本概念的更多相关文章
- Java学习笔记-多线程-创建线程的方式
创建线程 创建线程的方式: 继承java.lang.Thread 实现java.lang.Runnable接口 所有的线程对象都是Thead及其子类的实例 每个线程完成一定的任务,其实就是一段顺序执行 ...
- 黑马程序员——JAVA基础之多线程的线程间通讯等
------- android培训.java培训.期待与您交流! ---------- 线程间通讯: 其实就是多个线程在操作同一个资源,但是动作不同. wait(); 在其他线程调用此对象的notif ...
- Java基础之多线程篇(线程创建与终止、互斥、通信、本地变量)
线程创建与终止 线程创建 Thread类与Runnable接口的关系 public interface Runnable { public abstract void run(); } public ...
- java基础24 线程、多线程及线程的生命周期(Thread)
1.1.进程 正在执行的程序称作为一个进程.进程负责了内存空间的划分 疑问1:windows电脑称之为多任务的操作系统,那么Windows是同时运行多个应用程序呢? 从宏观的角度:windows确实在 ...
- 0038 Java学习笔记-多线程-传统线程间通信、Condition、阻塞队列、《疯狂Java讲义 第三版》进程间通信示例代码存在的一个问题
调用同步锁的wait().notify().notifyAll()进行线程通信 看这个经典的存取款问题,要求两个线程存款,两个线程取款,账户里有余额的时候只能取款,没余额的时候只能存款,存取款金额相同 ...
- 0036 Java学习笔记-多线程-创建线程的三种方式
创建线程 创建线程的三种方式: 继承java.lang.Thread 实现java.lang.Runnable接口 实现java.util.concurrent.Callable接口 所有的线程对象都 ...
- java 22 - 18 多线程之 线程的状态转换、线程组
线程的状态转换图解:图片 线程的线程组: 线程组: 把多个线程组合到一起. 它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制. 首先创建一个Runnable的实现类 publi ...
- Java基础学习——多线程之线程池
1.线程池介绍 线程池是一种线程使用模式.线程由于具有空闲(eg:等待返回值)和繁忙这种不同状态,当数量过多时其创建.销毁.调度等都会带来开销.线程池维护了多个线程,当分配可并发执行的任务时, ...
- 【温故而知新-万花筒】C# 异步编程 逆变 协变 委托 事件 事件参数 迭代 线程、多线程、线程池、后台线程
额基本脱离了2.0 3.5的时代了.在.net 4.0+ 时代.一切都是辣么简单! 参考文档: http://www.cnblogs.com/linzheng/archive/2012/04/11/2 ...
- java 22 - 20 多线程之线程池
程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互. 而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池. 线程池里的每一个线程代码结束后 ...
随机推荐
- bzoj 2257[Jsoi2009]瓶子和燃料 数论/裴蜀定理
题目 Description jyy就一直想着尽快回地球,可惜他飞船的燃料不够了. 有一天他又去向火星人要燃料,这次火星人答应了,要jyy用飞船上的瓶子来换.jyy 的飞船上共有 N个瓶子(1< ...
- QString:常用成员函数总结
QString是Qt中使用频率最高的几种数据类型之一,主要在于其提供了大量功能强大的成员函数,这里重点介绍一些常用的成员函数: 一.字符串处理相关 1.1 split() (拆分字符串) split( ...
- 贪心 Codeforces Round #173 (Div. 2) B. Painting Eggs
题目传送门 /* 题意:给出一种方案使得abs (A - G) <= 500,否则输出-1 贪心:每次选取使他们相差最小的,然而并没有-1:) */ #include <cstdio> ...
- Linux环境下SolrCloud集群环境搭建关键步骤
Linux环境下SolrCloud集群环境搭建关键步骤. 前提条件:已经完成ZooKeeper集群环境搭建. 一.下载介质 官网下载地址:http://www.apache.org/dyn/close ...
- jsp的简介
https://www.w3cschool.cn/jsp/jsp-intro.html
- 转】Mahout构建图书推荐系统
原博文出自于: http://blog.fens.me/hadoop-mahout-recommend-book/ 感谢! Mahout构建图书推荐系统 Hadoop家族系列文章,主要介绍Hadoop ...
- ATM机(非函数版)
#include<stdio.h>#include<stdlib.h>int main(void){char zhangHao[]="123";int mi ...
- Radis
http://www.redis.cn/ http://try.redis.io/ http://www.redisdoc.com/en/latest/ Redis 命令参考¶ 本文档是 Redis ...
- 构建微服务开发环境2————安装IntelliJ IDEA
[内容指引] 下载IntelliJ IDEA安装包: Mac版IDEA安装: Windows版IDEA按装. 一.下载IntelliJ IDEA安装包 访问官方网址:https://www.jetbr ...
- mysql中判断条件
if / case when 判断 SELECT CASE 1 WHEN 1 THEN "one" WHEN 2 THEN "two" ELSE "m ...