随着处理器的多核化,为提高处理器的资源利用率应用程序的并发变应运而生了.现在的操作系统是多任务操作系统,多线程是实现多任务的一种方式。

进程是指一个内存中运行的应用程序,每个进程都有自己独立的内存空间,一个进程可以启动多个线程。比如java.exe是一个进程,而java.exe中可以运行多个线程。进程中的多个线程共享进程的内存。

一:多线程的实现方法有二种

1. 实现Runnable接口

2. 继承Thread类

一个Thread类实例只是一个对象,与java中的其他对象一样,具有变量和方法,生死于堆上。java中每个线程都有一个调用栈,一个java应用总是从main方法开始运行的,main被称作为这个进程中的主线程。

线程分类:

1. 用户线程:当所有用户线程执行完毕的时候JVM自动关闭

2. 守护线程:由于守护线程不独立于JVM,守护线程一般都是由操作系统或用户自己创建的

创建线程方式应该没有直接贴代码来得更直接的了。

  实现Runnable接口方式创建线程

package NewThread;

public class DoSomething implements Runnable {

    private String name;
public DoSomething (String name) {
this.name = name;
} //重写run方法,此方法里面实现要执行的内容
@Override
public void run() {
// TODO Auto-generated method stub
for(long k = 0; k < 100; k++)
{
//Thread.currentThread() 获取当前线程的对象
//Thread.currentThread().getName() 获取当前线程的名字
System.out.println(Thread.currentThread().getName()+" : "+ name+":"+k);
}
} }
package NewThread;

public class TestRunnable {

    /**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
DoSomething ds1 = new DoSomething("张三");
DoSomething ds2 = new DoSomething("李四");
DoSomething ds3 = new DoSomething("王二"); Thread t1 = new Thread(ds1);
Thread t2 = new Thread(ds2);
Thread t3 = new Thread(ds3); //分别启动创建的三个线程
t1.start();
t2.start();
t3.start(); System.out.println("主线程执行完成");
} }

运行结果如下

Thread-0  :  张三:0
Thread-2 : 王二:0
主线程执行完成
Thread-1 : 李四:0
Thread-2 : 王二:1
Thread-0 : 张三:1
Thread-0 : 张三:2
Thread-0 : 张三:3
Thread-2 : 王二:2
Thread-1 : 李四:1
Thread-2 : 王二:3
Thread-0 : 张三:4
Thread-2 : 王二:4
Thread-1 : 李四:2
Thread-2 : 王二:5

发现打印的结果不是连续的,而且每次运行的结果都是不尽相同的;因为多个线程都是以轮流坐庄的方式占用CUP的,具体怎么坐庄以JVM线程调度程序为准

如果是单核处理器的话就没有必要写多线程的程序了,因为CUP每一时刻都只会把资源交给一个线程,而且还会无谓的增加栈的切换。

  下面介绍另一程序方式创建线程,继承Thread类创建线程

package NewThread;

public class DoSomething_Thread extends Thread {

    private String name;
public DoSomething_Thread (String name) {
this.name = name;
} //重写run方法,此方法里面实现要执行的内容
@Override
public void run() {
// TODO Auto-generated method stub
for(long k = 0; k < 100; k++)
{
//Thread.currentThread() 获取当前线程的对象
//Thread.currentThread().getName() 获取当前线程的名字
System.out.println(Thread.currentThread().getName()+" : "+ name+":"+k);
}
}
}
package NewThread;

public class TestThread {

	/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//实例化线程
Thread t1 = new DoSomething_Thread("张三");
Thread t2 = new DoSomething_Thread("李四");
Thread t3 = new DoSomething_Thread("王二"); //启动线程
t1.start();
t2.start();
t3.start(); System.out.println("主线程执行完毕!");
} }

运行结果如下

Thread-1  :  李四:0
Thread-2 : 王二:0
主线程执行完毕!
Thread-0 : 张三:0
Thread-2 : 王二:1
Thread-1 : 李四:1
Thread-1 : 李四:2
Thread-1 : 李四:3
Thread-1 : 李四:4
Thread-1 : 李四:5
Thread-2 : 王二:2
Thread-0 : 张三:1

线程创建就到此为止了。

线程状态:

线程在一定条件下,状态会发生变化。线程变化的状态转换图如下:

  1、新建状态(New):新创建了一个线程对象。

  2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

  3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

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

  (一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。

  (二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。

  (三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

  5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

注意:

1. 线程的名字是可以设置,若不设置则JVM会给线程一个名字;

2. 线程的结束是以run方法执行完毕为准;

3. 若线程启动了,它将不能再重新启动,线程只能被启动一次。

4. 线程的高度是JVM的一部分,在一个CUP的机器上实际上一次只能运行一个线程,一次只有一个线程栈被执行。JVM线程调度程序决定运行哪一个处于可执行状态下的线程

5. 尽管我们无法控制线程但我们可以通过别的方式来影响线程调试的方式

二:阻止线程执行

对于线程的阻止,在不考虑IO阻塞的情况下,有三种方式阻止线程执行

1. 睡眠:Thread.sleep(long millis)和Thread.sleep(long millis,int nanos);

  a. 线程睡眠是帮助所有线程获得运行机会的最好方法

  b.线程睡眠到期后会自动苏醒,并返回可运行状态,而不是运行状态,所以说当线程苏醒后并不能保证线程会立刻回到运行状态

  c.sleep()只能控制当前正在运行的线程

  d.若当前线程占用了锁,那么sleep是不会释放当前锁的

package NewThread;

public class DoSomething_Thread extends Thread {

    private String name;
public DoSomething_Thread (String name) {
this.name = name;
} //重写run方法,此方法里面实现要执行的内容
@Override
public void run() {
// TODO Auto-generated method stub
for(long k = 0; k < 100; k++)
{
//Thread.currentThread() 获取当前线程的对象
//Thread.currentThread().getName() 获取当前线程的名字
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" : "+ name+":"+k);
}
}
}
package NewThread;

public class TestThread {

    /**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//实例化线程
Thread t1 = new DoSomething_Thread("张三");
Thread t2 = new DoSomething_Thread("李四");
Thread t3 = new DoSomething_Thread("王二"); //启动线程
t1.start();
t2.start();
t3.start(); System.out.println("主线程执行完毕!");
} }

输出结果:这里我只把输出结果的部分贴出来了

主线程执行完毕!
Thread-1 : 李四:0
Thread-0 : 张三:0
Thread-2 : 王二:0
Thread-0 : 张三:1
Thread-1 : 李四:1
Thread-2 : 王二:1
Thread-0 : 张三:2
Thread-1 : 李四:2
Thread-2 : 王二:2
Thread-0 : 张三:3
Thread-2 : 王二:3
Thread-1 : 李四:3
Thread-0 : 张三:4
Thread-2 : 王二:4

分析结果可以看出每个线程都是循环得到cpu资源,这可能是因为每运行到一个线程都会sleep然后cpu资源换成了其他线程,从中可以看出一个线程不会连续打印二次。

2.线程的优先级和线程让步yield()

  a. 线程的让步是通过yield方法实现的,作用为暂停当前正在运行的线程对象,并执行其他线程,那我们先来了解一下线程的优先级。每个线程都有优先级,优先级的范围是

0~10之间,JVM线程的调度是基于优先级的抢先机制。一般情况下当前运行的线程的优先级是最大的,但这并不是绝对的。所以在设计多线程程序的时候也不能完全依赖于线程的优先级的设置。

package NewThread;

public class TestThread {

    /**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//实例化线程
Thread t1 = new DoSomething_Thread("张三");
Thread t2 = new DoSomething_Thread("李四");
Thread t3 = new DoSomething_Thread("王二");
t1.setName("线程1");
t2.setName("线程2");
t3.setName("线程3");
//启动线程 t1.setPriority(Thread.MIN_PRIORITY);
t2.setPriority(Thread.NORM_PRIORITY);
t3.setPriority(Thread.MAX_PRIORITY);
t1.start(); t2.start();
t3.start(); System.out.println("主线程执行完毕!");
} }

可以看看运行的结果,发现每次第一个运行的线程都不一定是优先级最高的线程,所以说依靠优先级设置线程的调度是很不可靠的

  b. Thread.yield方法的作用是为使当前运行的线程回到可执行的状态,使具有相同优先级的其他线程得以执行的机会。但使用yield方法也是不可靠的,因为此线程仍然有可能被JVM调度程序所命中而继续执行。这里yield与sleep的不同处是sleep的线程只有等到苏醒后才有可能会被选中,在睡眠之内肯定是让别的线程运行的。

3. join方法

join方法是让一个线程B加入到另外一个线程A的尾部,在A执行完成之前,B线程是不能运行的

package NewThread;

public class TestThread {

    /**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//实例化线程
Thread t1 = new DoSomething_Thread("张三");
Thread t2 = new DoSomething_Thread("李四");
Thread t3 = new DoSomething_Thread("王二");
t1.setName("线程1");
t2.setName("线程2");
t3.setName("线程3");
//启动线程
t1.start();
t2.start();
t3.start();
try {
t1.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("主线程执行完毕!");
} }

执行后可以发现,只有当线程1执行完成后才能主线程才会执行完毕,若把t1.join改成t1.join(10)则有可能会出现t1线程没有执行完毕主线程就已经执行完毕了,因为主线程会等待t1线程10毫秒,若过了10毫秒主线程也会等得不耐烦的,自己就去执行了。

三:线程的同步与锁

为了防止多个线程同时操作一个对象,从而对数据造成破坏。这方面就不多介绍了直接上例子吧

Java 并发--线程创建的更多相关文章

  1. Java 并发 线程属性

    Java 并发 线程属性 @author ixenos 线程优先级 1.每当线程调度器有机会选择新线程时,首先选择具有较高优先级的线程 2.默认情况下,一个线程继承它的父线程的优先级 当在一个运行的线 ...

  2. Java 并发 线程同步

    Java 并发 线程同步 @author ixenos 同步 1.异步线程本身包含了执行时需要的数据和方法,不需要外部提供的资源和方法,在执行时也不关心与其并发执行的其他线程的状态和行为 2.然而,大 ...

  3. Java 并发 线程的优先级

    Java 并发 线程的优先级 @author ixenos 低优先级线程的执行时刻 1.在任意时刻,当有多个线程处于可运行状态时,运行系统总是挑选一个优先级最高的线程执行,只有当线程停止.退出或者由于 ...

  4. Java 并发 线程的生命周期

    Java 并发 线程的生命周期 @author ixenos 线程的生命周期 线程状态: a)     New 新建 b)     Runnable 可运行 c)     Running 运行 (调用 ...

  5. Java并发--如何创建线程

    下面是本文的目录大纲: 一.Java中关于应用程序和进程相关的概念 二.Java中如何创建线程 三.Java中如何创建进程 转载原文链接:http://www.cnblogs.com/dolphin0 ...

  6. 从JDK源码角度看java并发线程的中断

    线程的定义给我们提供了并发执行多个任务的方式,大多数情况下我们会让每个任务都自行执行结束,这样能保证事务的一致性,但是有时我们希望在任务执行中取消任务,使线程停止.在java中要让线程安全.快速.可靠 ...

  7. Java并发-线程安全性

    首先了解一下多线程的概念 多线程:两段或以上的代码同时进行,多个顺序执行流. 并发和并行的区别 并发:做一下这个做一下那个. 并行:同时进行. 线程和进程的区别 进程:资源分配的基本单位,运行中的程序 ...

  8. Java并发——线程介绍

    前言: 互联网时代已经发展到了现在.从以前只考虑小流量到现在不得不去考虑高并发的问题.扯到了高并发的问题就要扯到线程的问题.你是否问过自己,你真正了解线程吗?还是你只知道一些其他博客里写的使用方法.下 ...

  9. java并发线程池---了解ThreadPoolExecutor就够了

    总结:线程池的特点是,在线程的数量=corePoolSize后,仅任务队列满了之后,才会从任务队列中取出一个任务,然后构造一个新的线程,循环往复直到线程数量达到maximumPoolSize执行拒绝策 ...

随机推荐

  1. 解题:CF570D Tree Requests

    题面 DSU on tree确实很厉害,然后这变成了一道裸题(逃 还是稍微说一下流程吧,虽然我那个模板汇总里写过 DSU on tree可以以$O(n\log n)$的复杂度解决树上子树统计问题,它这 ...

  2. 团体程序设计天梯赛 L2-028. 秀恩爱分得快

    1.输入-0(第一部分.第二部分),输出-02.只统计与两个人的亲密程度,否则超时 Data: 4 14 -0 1 -2 3-0 1 -0 1 ------ 4 1 3 1 -2 3-0 1 -0 1 ...

  3. [转]Multivariate Time Series Forecasting with LSTMs in Keras

    1. Air Pollution Forecasting In this tutorial, we are going to use the Air Quality dataset. This is ...

  4. P1077 摆花

    P1077 摆花 题目描述 小明的花店新开张,为了吸引顾客,他想在花店的门口摆上一排花,共 m 盆.通过调查顾客的喜好,小明列出了顾客最喜欢的 n 种花,从 1 到 n 标号.为了在门口展出更多种花, ...

  5. Tomcat权威指南-读书摘要系列1

    1. Tomcat的开幕式 1.1. Tomcat是以Java编写的 1.2. 以catalina命令启动和停止Tomcat .\catalina.bat start // 启动 .\catalina ...

  6. java基础-Integer类常用方法介绍

    java基础-Integer类常用方法介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 在实际程序使用中,程序界面上用户输入的数据都是以字符串类型进行存储的.而程序开发中,我们需 ...

  7. sql server查询数据库的大小和各数据表的大小

    查询出来的结果中各字段的详细说明参考MSDN资料:https://msdn.microsoft.com/zh-cn/library/ms188776.aspx 如果只是查询数据库的大小的话,直接使用以 ...

  8. 数据分析与展示---Pandas库数据特征分析

    说明:0轴axis=0和1轴axis=1 简介 一:数据的排序 二:数据的基本统计分析 三:数据的累积统计分析 四:数据的相关分析 一:数据的排序 a b c d a b c d 二:数据的基本统计分 ...

  9. python---基础知识回顾(五)(python2.7和python3.5中的编码)

    Unicode 和 UTF-8 有何区别? python基础之字符编码 以上两篇看懂即可,那下面的就不需要看了 python标准数据类型 Bytes python--数据类型bytes Python ...

  10. Openssh版本升级(Centos6.7)

    实现前提公司服务器需要进行安全测评,扫描漏洞的设备扫出了关于 openssh 漏洞,主要是因为 openssh的当前版本为5.3,版本低了,而yum最新的openssh也只是5.3,没办法只能到 rp ...