谈谈java中的线程(初级概念)
定义
关于进程与线程的定义 可参看一下这个介绍
http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html
在不细抠定义的情况下
我们可以认为 在操作系统里一个任务就是一个进程 像word,qq都可以看做一个进程.
另一方面如果这个进程内部的函数调用 就是一条线 那它就是单线程
如果有多条线 那就是多线程 而在这个进程内部 每一条执行的流程就叫做一个线程
我们自己定义的线程
在自定义线程之前 我们先看看java里关于线程的一些类
主要有两个
一个是interface Runnable
里面只有一个方法run()
一个是class Thread
其实它也实现了Runnable接口(也就必须重写了run方法),同时它还有一个方法叫start
来我们看第一个例子 顺便讲讲start与run的区别
public class TestThread1 { public static void main(String args[]) { Runner1 r = new Runner1(); r.start(); //r.run(); for(int i=0; i<100; i++) { System.out.println("Main Thread:------" + i); } } } class Runner1 extends Thread { public void run() { for(int i=0; i<100; i++) { System.out.println("Runner1 :" + i); } } }
运行的结果是 Main Thread..与Runner1...交替输出
这时候 就运行了两个线程 一个主线程 一个r线程
如果把r.start改成r.run那么就是先打出100个Runner1..然后才是100个Main Thread
为什么?
大家仔细看看如果我们调用r.run() 那不就是函数调用了么!!!
所以一句话我们自定义的线程必须实现run方法 但调用的时候得是start()!
再看另一种定义线程的方式
public class TestThread1 { public static void main(String args[]) { Runner1 r = new Runner1(); Thread t = new Thread(r); t.start(); for(int i=0; i<50; i++) { System.out.println("Main Thread:------" + i); } } } class Runner1 implements Runnable { public void run() { for(int i=0; i<50; i++) { System.out.println("Runner1 :" + i); } } }
两种定义线程的方式 我们选择哪一种?
选第二种 实现Runable接口的方式
因为我们一旦继承了Thread类 就不能再继承别的类了
因此能继承类能实现接口的时候就选实现接口的方式
*************************************************
以下为2016-04-12日补充
那么使用实现implement的方法,还有什么好处呢?
可以实现多个线程处理一个资源
什么意思?
请写一个买票的代码
SellTicket类里面有一个成员变量ticketCount是int型的,或者说是AtomicInteger
run方法不断的对ticketCount进行减一操作
如果实现runnable,那我new一个SellTicket放到不同的Thread里面就OK
可是如果是继承Thread类,那么我就得new多个SellTicket
那么ticketCount又是几个呢?
以上为2016-04-12日补充
*************************************************
sleep interrupt stop flag
public class TestThread3{ public static void main(String args[]) { Runner3 r = new Runner3(); Thread t = new Thread(r); t.start(); } } class Runner3 implements Runnable { public void run() { for(int i=0; i<30; i++) { if(i%10==0 && i!=0) { try{ Thread.sleep(2000); }catch(InterruptedException e){} } System.out.println("No. " + i); } } }
看这个例子 它运行的结果是 先马上打印出0-9然后停两秒再打印出10-19...
Thread.sleep()就是让程序休眠一段时间 时间的长短由参数指定 单位为毫秒
不过要注意 sleep会抛出InterruptedException异常
public class TestInterrupt { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); try {Thread.sleep(10000);} catch (InterruptedException e) {} thread.interrupt(); } } class MyThread extends Thread { boolean flag = true; public void run(){ while(true){ System.out.println("==="+new Date()+"==="); try { sleep(1000); } catch (InterruptedException e) { return; } } } }
这段代码的结果是 每隔一秒输出当前的时间 等运行了10秒后停止
代码写到这里的时候,如何脑子里蹦出一个问题,如果一个线程在sleep的时候,如果传递的参数是5000,那这个5000是什么意思?
第一:这个线程开始进入阻塞队列,当"自然时间(也就是说你看着自己的手表,来读时间)"过了5000毫秒后,这个线程再从阻塞队列出来进入就绪队列,等待时间片
第二:这个线程就睡着了,时间片来了,我继续睡着,cpu被我独占,即使我在睡觉,cpu也得在这等我
感觉好像应该是第一种情况
那么请证明之:
看代码
public static void main(String[] args) { for (int i = 0; i < 100; i++) { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"已经结束了"); } }).start(); } }
上面代码运行的结果是,启动5s后,一瞬间打印出
Thread-xx已经结束了
上面的100句话
这说明是进入了阻塞队列,在线程睡觉的时候,jvm的调度器就完全不管那些睡觉的线程了
我们再看下面的代码
public class SleepTest implements Runnable { private Object lock; public SleepTest(Object lock){ this.lock=lock; } public static void main(String[] args) { Object lock=new Object(); for (int i = 0; i < 10; i++) { new Thread(new SleepTest(lock)).start(); } } @Override public void run() { synchronized (lock) { try { Thread.sleep(1000); System.out.println(Thread.currentThread().getName()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
结果是大概每隔1s,输出一个Thread-x
上面的两个例子说明
线程sleep的时候,释放了对cpu的控制,但是没有释放对锁的控制
关于sleep的更多知识,请参见
大家也看到了 sleep本身可能会被"打扰" 就是interrupt 我们在主线程里调用了 thread.interrupt();
等于就抛出了异常 那么thread就只能执行catch里面的代码 return了
public class InterruptTest { public static void main(String[] args) throws InterruptedException { MyThread t = new MyThread("MyThread"); t.start(); Thread.sleep(100);// 睡眠100毫秒 t.interrupt();// 中断t线程 } } class MyThread extends Thread { int i = 0; public MyThread(String name) { super(name); } public void run() { while(true) {// 死循环,等待被中断 System.out.println(getName() + getId() + "执行了" + ++i + "次"); } } }
程序会一直执行下去么?
会. 为什么?
intertupt只是打断睡眠,它不是终止进程
看下面这个
public class InterruptTest { public static void main(String[] args) throws InterruptedException { MyThread t = new MyThread("MyThread"); t.start(); Thread.sleep(100);// 睡眠100毫秒 t.interrupt();// 中断t线程 } } class MyThread extends Thread { int i = 0; public MyThread(String name) { super(name); } public void run() { while(!isInterrupted()) {// 当前线程没有被中断,则执行 System.out.println(getName() + getId() + "执行了" + ++i + "次"); } } }
另外 Thread还有一个方法叫stop(已废弃) 看名字 我们就知道它能干什么了
不过他比interrupt还粗暴 interrupt毕竟还有一个catch呢 在结束之前 还能干点事 stop就完全没有做其他事的机会了
当然要结束这种"死循环"的线程 也不难
MyThread里面加一个Boolean型的flag 令其为true while(true)改成while(flag)
想结束的时候 在主函数里让flag=false 即可
这里就是把interrupt改成两个setFlag. 当然对那个flag的设值的名字可以改为shutdown
join yield priority
public class TestJoin { public static void main(String[] args) { MyThread2 t1 = new MyThread2("abcde"); t1.start(); try { t1.join(); } catch (InterruptedException e) {} for(int i=1;i<=10;i++){ System.out.println("i am main thread"); } } } class MyThread2 extends Thread { MyThread2(String s){ super(s); } public void run(){ for(int i =1;i<=10;i++){ System.out.println("i am "+getName()); try { sleep(300); } catch (InterruptedException e) { return; } } } }
通过查看这个代码的结果 就明白 join就是把线程合并
如上 就是等ti的run执行完毕后 主线程再继续往下走 有点把线程调用看成函数调用的感觉
至于Thread的yield()方法就是线程主动放弃他所拥有的时间片 让其他线程使用 (当然只是放弃一次 下一次有了时间片 它就不放弃了)
代码如下
public class TestYield { public static void main(String[] args) { MyThread3 t1 = new MyThread3("t1"); MyThread3 t2 = new MyThread3("t2"); t1.start(); t2.start(); } } class MyThread3 extends Thread { MyThread3(String s){super(s);} public void run(){ for(int i =1;i<=40;i++){ System.out.println(getName()+": "+i); if(i%10==0){ yield(); } } } }
运行时会发现 每当一个线程打印出自己的名字和整10的序号的时候 下一个运行的都不是自己 yield() 它放弃了本次对时间片的使用
至于priority(优先级)
有两个方法setPriority getPriority
priority 从1到10 10为最高
priority越高 获得时间片的几率越大
代码如下
public class TestPriority { public static void main(String[] args) { Thread t1 = new Thread(new T1()); Thread t2 = new Thread(new T2()); t1.setPriority(Thread.NORM_PRIORITY + 3); t1.start(); t2.start(); } } class T1 implements Runnable { public void run() { for(int i=0; i<1000; i++) System.out.println("T1: " + i); } } class T2 implements Runnable { public void run() { for(int i=0; i<1000; i++) System.out.println("------T2: " + i); } }
sleep与yield
线程Asleep的时候,下一个时间片可以给任何一个线程
线程Ayield的时候,本来A线程所拥有的时间片会在与线程A同优先级的线程间转换
参考资料
http://blog.csdn.net/ghsau/article/details/17560467
谈谈java中的线程(初级概念)的更多相关文章
- 谈谈JAVA中的安全发布
谈谈JAVA中的安全发布 昨天看到一篇文章阐述技术类资料的"等级",看完之后很有共鸣.再加上最近在工作中越发觉得线程安全性的重要性和难以捉摸,又掏出了<Java并发编程实战& ...
- 浅谈利用同步机制解决Java中的线程安全问题
我们知道大多数程序都不会是单线程程序,单线程程序的功能非常有限,我们假设一下所有的程序都是单线程程序,那么会带来怎样的结果呢?假如淘宝是单线程程序,一直都只能一个一个用户去访问,你要在网上买东西还得等 ...
- Java中的线程同步
Java 中的线程同步问题: 1. 线程同步: 对于访问同一份资源的多个线程之间, 来进行协调的这个东西. 2. 同步方法: 当某个对象调用了同步方法时, 该对象上的其它同步方法必须等待该同步方法执行 ...
- Java多线程编程(1)--Java中的线程
一.程序.进程和线程 程序是一组指令的有序集合,也可以将其通俗地理解为若干行代码.它本身没有任何运行的含义,它只是一个静态的实体,它可能只是一个单纯的文本文件,也有可能是经过编译之后生成的可执行文 ...
- 【万字图文-原创】 | 学会Java中的线程池,这一篇也许就够了!
碎碎念 关于JDK源码相关的文章这已经是第四篇了,原创不易,粉丝从几十人到昨天的666人,真的很感谢之前帮我转发文章的一些朋友们. 从16年开始写技术文章,到现在博客园已经发表了222篇文章,大多数都 ...
- 并发王者课 - 青铜 2:峡谷笔记 - 简单认识Java中的线程
在前面的<兵分三路:如何创建多线程>文章中,我们已经通过Thread和Runnable直观地了解如何在Java中创建一个线程,相信你已经有了一定的体感.在本篇文章中,我们将基于前面的示例代 ...
- 【Java中的线程】java.lang.Thread 类分析
进程和线程 联想一下现实生活中的例子--烧开水,烧开水时是不是不需要在旁边守着,交给热水机完成,烧开水这段时间可以去干一点其他的事情,例如将衣服丢到洗衣机中洗衣服.这样开水烧完,衣服洗的也差不多了.这 ...
- Java中的线程
http://hi.baidu.com/ochzqvztdbabcir/item/ab9758f9cfab6a5ac9f337d4 相濡以沫 Java语法总结 - 线程 一 提到线程好像是件很麻烦很复 ...
- [译]线程生命周期-理解Java中的线程状态
线程生命周期-理解Java中的线程状态 在多线程编程环境下,理解线程生命周期和线程状态非常重要. 在上一篇教程中,我们已经学习了如何创建java线程:实现Runnable接口或者成为Thread的子类 ...
随机推荐
- Ubuntu等Linux系统显卡性能测试软件 Unigine 3D
Ubuntu等Linux系统显卡性能测试软件 Unigine 3D Ubuntu Intel显卡驱动安装,请参考: http://blog.csdn.net/zhangrelay/article/de ...
- 用reg文件把便携版sublime text 3添加到右键菜单
假设sublime文件夹在C:\\Users\\T430i\\Downloads\\Sublime Text Build 3059 x64\\ 则: Windows Registry Editor V ...
- JavaMail API 概述
JavaMail API提供了一种与平台无关和协议独立的框架来构建邮件和消息应用程序. JavaMail API提供了一组抽象类定义构成一个邮件系统的对象.它是阅读,撰写和发送电子信息的可选包(标准扩 ...
- Maven简介(Maven是什么)
简介 Maven,在意第绪语中意为对知识的积累.Maven最初用来在Jakarta Turbine项目中简化该项目的构建过程.Jakarta Trubine项目有多个工程,每个工程都有自己的多个Ant ...
- EBS业务学习之应收管理
Oracle Receivable 是功能完备地应收款管理系统,它能够有效地管理客户.发票和收帐过程,因此是财务模块的重要组成部分,是财务系统中较为核心的模块之一.对于一个公司来说,是否能够与客户保持 ...
- MapReduce:并行计算框架
MapReduce 是 Hadoop 的核心组成,是专用于进行数据计算的.重点掌握实现 MapReduce 算法的步骤,掌握 map.reduce 函数的特点.如何写函数. 如果我们把 MapRedu ...
- 1091. Acute Stroke (30)
题目如下: One important factor to identify acute stroke (急性脑卒中) is the volume of the stroke core. Given ...
- [maven学习笔记]第一节,认识maven,搭建maven开发环境,写第一个HelloWorld
本文地址:http://blog.csdn.net/sushengmiyan/article/details/40142771 maven官网:http://maven.apache.org/ 学习视 ...
- Xcode中使用数据(硬件)断点调试
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 在Xcode的GUI界面中只能添加软断点,而无法增加硬断点.但 ...
- Hibernate超简单多表操作
所谓一对多映射 在数据库中我们通常会通过添加外键的方式将表关联起来,表现一对多的关系. 而在Hibernate中,我们则要通过在一方持有多方的集合来实现,即在"一"的一端中使用元素 ...