Java 多线程详解(一)------ 概念的引入:http://www.cnblogs.com/ysocean/p/6882988.html
在上一篇博客中,我们已经介绍了并发和并行的区别,以及进程和线程的理解,那么在 Java 中如何创建进程和线程呢?
1、在 Windows 操作系统中创建进程
在 windows 操作系统中,我们创建一个进程通常就是打开某个应用软件,这便在电脑中创建了一个进程。更原始一点的,我们在命令提示符中来做(我们以打开记事本这个进程为例):
第一步:windows+R, 输入 cmd, 打开 cmd 命令提示符
第二步:在命令提示符中输入 notepad, 按 Enter 键便会弹出记事本应用软件
PS:常用的 windows 应用软件命令
1、regedit:打开注册表编辑器
2、control:打开控制面板
3、msconfig:打开系统配置
4、gpedit.msc:打开本地组策略
5、explorer:打开资源管理器
6、taskmgr:任务管理器
7、logoff:直接注销计算机
8、osk:打开屏幕键盘
9、calc:打开计算器
10、mspaint:调出画图软件
11、dxdiag:查看电脑详细配置信息
12、mstsc:打开远程桌面连接
13、systeminfo:查看计算机基本信息
14、notepad:打开记事本
2、在 Java 中创建进程
第一种方法:通过 Runtime 类的 exec() 方法来创建进程
- public class Runtime
- extends Object①、表示当前进程所在的虚拟机实例,每个Java应用程序都有一个Runtime类的Runtime ,允许应用程序与运行应用程序的环境进行接口。②、由于任何进程只会运行与一个虚拟机实例当中,即只会产生一个虚拟机实例(底层源码采用 单例模式)③、当前运行时可以从getRuntime方法获得。
由上面源码可以看到,构造器私有化了,即外部我们不能 new 一个新的 Runtime 实例,而内部给了我们一个获取 Runtime 实例的方法 getRuntime() 。
通过 Runtime 类创建一个 记事本的 进程
- public class ProcessTest {
- public static void main(String[] args) throws Exception {
- Runtime run = Runtime.getRuntime();
- //打开记事本
- run.exec("notepad");
- }
- }
第二种方法:通过 ProcessBuilder 创建线程
- public final class ProcessBuilder
- extends Object①、此类用于创建操作系统进程。
- ②、每个ProcessBuilder实例管理进程属性的集合。 start()方法使用这些属性创建一个新的Process实例。 start()方法可以从同一实例重复调用,以创建具有相同或相关属性的新子进程。
- public class ProcessTest {
- public static void main(String[] args) throws Exception {
- //打开记事本
- ProcessBuilder pBuilder = new ProcessBuilder("notepad");
- pBuilder.start();
- }
- }
3、在 Java 中创建线程
第一种方法:继承 Thread 类
- public class Thread
- extends Object
- implements Runnable
步骤:1、定义一个线程类 A 继承于 java.lang.Thread 类
2、在 A 类中覆盖 Thread 类的 run() 方法
3、在 run() 方法中编写需要执行的操作
4、在 main 方法(线程)中,创建线程对象,并启动线程
创建线程类:A 类 a = new A() 类;
调用 start() 方法启动线程:a.start();
- package com.ys.thread;
- class Thread1 extends Thread{
- @Override
- public void run() {
- for(int i = 0 ; i < 10 ;i++){
- System.out.println("播放音乐"+i);
- }
- }
- }
- public class ThreadTest {
- public static void main(String[] args) {
- for(int i = 0 ; i < 10 ; i++){
- System.out.println("玩游戏"+i);
- if(i==5){
- Thread1 th1 = new Thread1();
- th1.start();
- }
- }
- }
- }
结果:
- 玩游戏0
- 玩游戏1
- 玩游戏2
- 玩游戏3
- 玩游戏4
- 玩游戏5
- 玩游戏6
- 玩游戏7
- 玩游戏8
- 玩游戏9
- 播放音乐0
- 播放音乐1
- 播放音乐2
- 播放音乐3
- 播放音乐4
- 播放音乐5
- 播放音乐6
- 播放音乐7
- 播放音乐8
- 播放音乐9
注意:我们看结果,并不是出现 5 个先打游戏,然后在播放音乐,这是线程调度的结果,两个线程同时在争抢 CPU 的资源,即最后的结果,前面 5 个打游戏的必然先出现的,后面的啥时候出现播放音乐就看 CPU 怎么调度了,这是随机的。我们不能干涉。
第二种方法:实现 Runnable 接口
- @FunctionalInterface
- public interface Runnable
1、Runnable 接口应由任何类实现,其实例将由线程执行。 该类必须定义一个无参数的方法,称为 run 。 2、该接口旨在为希望在活动时执行代码的对象提供一个通用协议。此类整个只有一个 run() 抽象方法
步骤:1、定义一个线程类 A 实现于 java.lang.Runnable 接口(注意:A 类不是线程类, 没有 start() 方法,不能直接 new A 的实例启动线程)
2、在 A 类中覆盖 Runnable 接口的 run() 方法
3、在 run() 方法中编写需要执行的操作
4、在 main 方法(线程)中,创建线程对象,并启动线程
创建线程类:Thread t = new Thread(new A 类 () ) ;
调用 start() 方法启动线程:t.start();
- class Runnable1 implements Runnable{
- @Override
- public void run() {
- for(int i = 0 ; i < 10 ;i++){
- System.out.println("播放音乐"+i);
- }
- }
- }
- public class RunnableTest {
- public static void main(String[] args) {
- for(int i = 0 ; i < 10 ; i++){
- System.out.println("玩游戏"+i);
- if(i==5){
- Thread th1 = new Thread(new Runnable1());
- th1.start();
- }
- }
- }
- }
第三种方法:使用匿名内部类创建线程
- public static void main(String[] args) {
- for(int i = 0 ; i < 10 ; i++){
- System.out.println("玩游戏"+i);
- if(i==5){
- new Thread(new Runnable() {
- @Override
- public void run() {
- for(int i = 0 ; i < 10 ;i++){
- System.out.println("播放音乐"+i);
- }
- }
- }).start();
- }
- }
- }
注意:
1、启动线程是调用 start() 方法,而不是 调用 run() 方法。
解析:run() 方法: 在本线程内调用 run() 方法,和其他方法没有什么区别,可以重复多次调用;
start() 方法: 启动一个线程,实际上还是调用该 Runnable 对象的 run() 方法。
打开 Thread 类的源码,start() 方法里面有一句:
private native void start0(); //native 关键字:本地程序调用
native 关键字指的是 Java 本地接口调用,即是使用 Java 调用本地操作系统的函数功能完成一些特殊的操作,而这样的代码开发在 Java 中几乎很少出现,因为 Java 的最大特点是可移植性,如果一个程序 只能在固定的操作系统上使用,那么可移植性就将彻底丧失,多线程的实现一定需要操作系统的支持,那么 start0() 方法实际上就和抽象方法很类似,没有方法体,而是交给 JVM 去实现,即在 windows 下的 JVM 可能使用 A 方法实现 start0(),在 linux 下的 JVM 可能使用 B 方法实现 start0(),在调用时并不会关心具体是何方式实现了 start0() 方法,只会关心最终的操作结果,交给 JVM 去匹配了不同的操作系统。
2、不能多次启动同一个线程,即多次调用 start() 方法,只能调用一次,否则报错:
来源: http://www.cnblogs.com/ysocean/p/6883491.html