当前位置: 首页 > news >正文

java 多线程 - 1

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

一、概述

现在的操作系统是 多任务操作系统。多线程是实现多任务的一种方式。

进程 是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程。比如在Windows系统中,一个运行的exe就是一个进程。

线程 是指进程中的一个执行流程,一个进程中可以运行多个线程。比如java.exe进程中可以运行很多线程。线程总是属于某个进程,进程中的多个线程共享进程的内存。

单线程:程序中只存在一个线程,实际上主方法就是一个主线程

多线程:在一个程序中运行多个任务,目的是更好地使用CPU资源

二、线程的实现

1、继承 Thread类,重写 run方法

class MyThread extends Thread{
    
	private static int num = 0;
 
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() +" 主动创建的第【"+(++num)+"】个线程");
    }
    
    public static void main(String[] args) {
		MyThread myThread1 = new MyThread();
		MyThread myThread2 = new MyThread();
		myThread1.setName("Thread - name1");
		myThread1.start();
		myThread2.setName("Thread - name2");
		myThread2.start();
		myThread1.run();
		 /***
	     * console 结果:
	     * main 主动创建的第【2】个线程
           Thread - name1 主动创建的第【1】个线程
           Thread - name2 主动创建的第【3】个线程
	     */
    }
}

说明:创建好了自己的线程类之后,就可以创建线程对象了,然后通过start()方法去启动线程。需要注意,不是调用run()方法启动线程,run方法中只是定义需要执行的任务,如果直接调用run方法,即相当于在主线程中执行run方法,跟普通的方法调用没有任何区别,此时并不会创建一个新的线程来执行定义的任务。

2、实现 Runnable 接口,重写 run 方法

public class MyRunnable implements Runnable{

	public void run() {
		System.out.println(Thread.currentThread().getName() + " 线程,执行...");
	}

	public static void main(String[] args) {
		MyRunnable myRunnable = new MyRunnable();
		Thread myThread = new Thread(myRunnable);
		myThread.setName("Thread - my1");
		myThread.start();
		
		myRunnable.run();
		
		/**Console结果:
		 * main 线程,执行...
		 * Thread - my1 线程,执行...
		 */
	}
}

说明:Runnable的中文意思是“任务”,顾名思义,通过实现Runnable接口,我们定义了一个子任务,然后将子任务交由Thread去执行。注意,这种方式必须将Runnable作为Thread类的参数,然后通过Thread的start方法来创建一个新线程来执行该子任务。如果调用Runnable的run方法的话,是不会创建新线程的,这根普通的方法调用没有任何区别。事实上,查看Thread类的实现源代码会发现Thread类是实现了Runnable接口的。

三、线程的状态

  • 创建(new)状态: 准备好了一个多线程的对象
  • 就绪(runnable)状态: 调用了start()方法, 等待CPU进行调度
  • 运行(running)状态: 执行run()方法
  • 阻塞(blocked)状态: 暂时停止执行, 可能将资源交给其它线程使用
  • 终止(dead)状态: 线程销毁

线程 从 创建 -》消亡 过程:

说明:在有些教程上将blocked、waiting、time waiting统称为阻塞状态,这个也是可以的,只不过这里将线程的状态和Java中的方法调用联系起来,所以将waiting和time waiting两个状态分离出来。

sleep和wait的区别:

  • sleepThread类的方法,waitObject类中定义的方法.
  • Thread.sleep不会导致锁行为的改变, 如果当前线程是拥有锁的, 那么Thread.sleep不会让线程释放锁.
  • Thread.sleepObject.wait都会暂停当前的线程. OS会将执行时间分配给其它线程. 区别是, 调用wait后, 需要别的线程执行notify/notifyAll才能够重新获得CPU执行时间.

上下文切换 

对于单核CPU来说(对于多核CPU,此处就理解为一个核),CPU在一个时刻只能运行一个线程,当在运行一个线程的过程中转去运行另外一个线程,这个叫做线程上下文切换(对于进程也是类似)。

sleep()方法

方法sleep()的作用是在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行)。这个“正在执行的线程”是指this.currentThread()返回的线程。

sleep相当于让线程睡眠,交出CPU,让CPU去执行其他的任务。
但是有一点要非常注意,sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象

public class MyThread2 implements Runnable{

	private int i = 0;
	
	public void run() {
	   System.out.println(Thread.currentThread().getName() + "开始..");
	   i = getValue();
	   System.out.println(Thread.currentThread().getName() + "结束..");
	}

	public synchronized int getValue(){
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + ",i=" + ++i);
		return i;
	}
}

public class MyThread2Test {

	public static void main(String[] args) {
		MyThread2 myThread2 = new MyThread2();
		Thread thread1 = new Thread(myThread2);
		Thread thread2 = new Thread(myThread2);
		
		thread1.start();
		thread2.start();
		/**Console结果:
		 * Thread-0开始..
		   Thread-1开始..
		   Thread-0,i=1
		   Thread-0结束..
		   Thread-1,i=2
		   Thread-1结束..
		 * 
		 */
	}
}

示例说明:从上面输出结果可以看出,当Thread-0进入睡眠状态之后,Thread-1并没有去执行具体的任务。只有当Thread-0执行完之后,此时Thread-0释放了锁,Thread-1才开始执行。

yield()方法

调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。但是yield不能控制具体的交出CPU的时间,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。

注意,调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。

public class MyThread3 implements Runnable{
	
	public void run() {
		long beginTime = System.currentTimeMillis();
		int count=0;
        for (int i=0;i<50000000;i++){
            count=count+(i+1);
            Thread.yield();
        }
		long endTime = System.currentTimeMillis();
		System.out.println("用时:"+(endTime-beginTime)+" 毫秒!");
	}

	public static void main(String[] args) {
		MyThread3 myThread3 = new MyThread3();
		Thread thread = new Thread(myThread3);
		thread.start();
	}
}

执行结果:

1

用时:3 毫秒!

如果将 //Thread.yield();的注释去掉,执行结果如下:

1

用时:16080 毫秒!

 start() 方法

start()用来启动一个线程,当调用start方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源。

run() 方法

run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。注意,继承Thread类必须重写run方法,在run方法中定义具体要执行的任务。

getId()方法

getId()的作用是取得线程的唯一标识.

join()方法

在很多情况下,主线程创建并启动了线程,如果子线程中进行大量耗时运算,主线程往往将早于子线程结束之前结束。这时,如果主线程想等待子线程执行完成之后再结束,比如子线程处理一个数据,主线程要取得这个数据中的值,就要用到join()方法了。方法join()的作用是等待线程对象销毁。

/**
 * join() 方法测试
 */
public class MyThread4 extends Thread{
    public MyThread4(String name) {
        super(name);
    }
    
    public void run() {
    	int count = 0;
        for (int i = 0; i < 5; i++) {
            count += i;
        }
        System.out.println(getName() + "  " + count);
    }
    
    public static void main(String[] args) throws InterruptedException {
        // 启动子进程
        new MyThread4("new thread").start();
        for (int i = 0; i < 10; i++) {
           if (i == 5) {
            	MyThread4 th = new MyThread4("joined thread");
                th.start();
                th.join();
           }
           System.out.println(Thread.currentThread().getName() + "  " + i);
        }
        
        /**
         * Console:
         *  main  0
			main  1
			main  2
			main  3
			main  4
			new thread  10
			joined thread  10
			main  5
			main  6
			main  7
			main  8
			main  9
         */
    }
}

说明:由上可以看出main主线程等待joined thread线程先执行完了才结束的。

守护线程  setDaemon 和 isDaemon

守护线程和用户线程的区别 在于:守护线程依赖于创建它的线程,而用户线程则不依赖。举个简单的例子:如果在main线程中创建了一个守护线程,当main方法运行完毕之后,守护线程也会随着消亡。而用户线程则不会,用户线程会一直运行直到其运行完毕。在JVM中,像垃圾收集器线程就是守护线程。另外,thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。 

线程优先级

在操作系统中,线程可以划分优先级,优先级较高的线程得到的CPU资源较多,也就是CPU优先执行优先级较高的线程对象中的任务。
设置线程优先级有助于帮“线程规划器”确定在下一次选择哪一个线程来优先执行。
设置线程的优先级使用setPriority()方法。

线程优先级特性:

  • 继承性
    比如A线程启动B线程,则B线程的优先级与A是一样的。
  • 规则性
    高优先级的线程总是大部分先执行完,但不代表高优先级线程全部先执行完。
  • 随机性
    优先级较高的线程不一定每一次都先执行完。

 

 

转载于:https://my.oschina.net/u/1387400/blog/1512144

相关文章:

  • Teradata“统一数据架构”引领企业大数据应用体系
  • 项目经理案头手册学习系列【2】
  • 重要的转变(感觉自己最近堕落必读)
  • 2008 SQL Server优化(2)-改善SQL语句
  • ScrollView子控件高度设置无效
  • Python字典
  • 彻底搞清楚C/C++中日期和时间 time_t与struct tm,time(NULL),ctime;strftime
  • 修改博客园主题
  • Expression Blend实例中文教程(13) - 控件模板快速入门ControlTemplates
  • 微软Azure 文档地址
  • [转]优化MySQL数据库性能的八大“妙手”
  • 罗湖致力打造智慧城市标杆区
  • 转:Win32 同步机制摘要
  • create raid5
  • 简析几何叉乘与安培力的内在逻辑
  • JS中 map, filter, some, every, forEach, for in, for of 用法总结
  • 「译」Node.js Streams 基础
  • 【跃迁之路】【641天】程序员高效学习方法论探索系列(实验阶段398-2018.11.14)...
  • 【跃迁之路】【669天】程序员高效学习方法论探索系列(实验阶段426-2018.12.13)...
  • 2017届校招提前批面试回顾
  • 5、React组件事件详解
  • ComponentOne 2017 V2版本正式发布
  • electron原来这么简单----打包你的react、VUE桌面应用程序
  • ERLANG 网工修炼笔记 ---- UDP
  • es的写入过程
  • js递归,无限分级树形折叠菜单
  • Linux gpio口使用方法
  • node-glob通配符
  • Node项目之评分系统(二)- 数据库设计
  • PHP 使用 Swoole - TaskWorker 实现异步操作 Mysql
  • Phpstorm怎样批量删除空行?
  • Vue UI框架库开发介绍
  • windows下如何用phpstorm同步测试服务器
  • yii2权限控制rbac之rule详细讲解
  • 第三十一到第三十三天:我是精明的小卖家(一)
  • 多线程 start 和 run 方法到底有什么区别?
  • 深入浅出Node.js
  • 使用Tinker来调试Laravel应用程序的数据以及使用Tinker一些总结
  • 译自由幺半群
  • 再次简单明了总结flex布局,一看就懂...
  • ​LeetCode解法汇总2182. 构造限制重复的字符串
  • ​Python 3 新特性:类型注解
  • #{} 和 ${}区别
  • #我与Java虚拟机的故事#连载18:JAVA成长之路
  • ( 10 )MySQL中的外键
  • (1)(1.13) SiK无线电高级配置(五)
  • (2)(2.4) TerraRanger Tower/Tower EVO(360度)
  • (AngularJS)Angular 控制器之间通信初探
  • (rabbitmq的高级特性)消息可靠性
  • (SpringBoot)第七章:SpringBoot日志文件
  • (二)基于wpr_simulation 的Ros机器人运动控制,gazebo仿真
  • (终章)[图像识别]13.OpenCV案例 自定义训练集分类器物体检测
  • (转)全文检索技术学习(三)——Lucene支持中文分词
  • (转)使用VMware vSphere标准交换机设置网络连接
  • .【机器学习】隐马尔可夫模型(Hidden Markov Model,HMM)