定义

关于进程与线程的定义 可参看一下这个介绍

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的更多知识,请参见

理解 Thread.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中的线程(初级概念)的更多相关文章

  1. 谈谈JAVA中的安全发布

    谈谈JAVA中的安全发布 昨天看到一篇文章阐述技术类资料的"等级",看完之后很有共鸣.再加上最近在工作中越发觉得线程安全性的重要性和难以捉摸,又掏出了<Java并发编程实战& ...

  2. 浅谈利用同步机制解决Java中的线程安全问题

    我们知道大多数程序都不会是单线程程序,单线程程序的功能非常有限,我们假设一下所有的程序都是单线程程序,那么会带来怎样的结果呢?假如淘宝是单线程程序,一直都只能一个一个用户去访问,你要在网上买东西还得等 ...

  3. Java中的线程同步

    Java 中的线程同步问题: 1. 线程同步: 对于访问同一份资源的多个线程之间, 来进行协调的这个东西. 2. 同步方法: 当某个对象调用了同步方法时, 该对象上的其它同步方法必须等待该同步方法执行 ...

  4. Java多线程编程(1)--Java中的线程

    一.程序.进程和线程   程序是一组指令的有序集合,也可以将其通俗地理解为若干行代码.它本身没有任何运行的含义,它只是一个静态的实体,它可能只是一个单纯的文本文件,也有可能是经过编译之后生成的可执行文 ...

  5. 【万字图文-原创】 | 学会Java中的线程池,这一篇也许就够了!

    碎碎念 关于JDK源码相关的文章这已经是第四篇了,原创不易,粉丝从几十人到昨天的666人,真的很感谢之前帮我转发文章的一些朋友们. 从16年开始写技术文章,到现在博客园已经发表了222篇文章,大多数都 ...

  6. 并发王者课 - 青铜 2:峡谷笔记 - 简单认识Java中的线程

    在前面的<兵分三路:如何创建多线程>文章中,我们已经通过Thread和Runnable直观地了解如何在Java中创建一个线程,相信你已经有了一定的体感.在本篇文章中,我们将基于前面的示例代 ...

  7. 【Java中的线程】java.lang.Thread 类分析

    进程和线程 联想一下现实生活中的例子--烧开水,烧开水时是不是不需要在旁边守着,交给热水机完成,烧开水这段时间可以去干一点其他的事情,例如将衣服丢到洗衣机中洗衣服.这样开水烧完,衣服洗的也差不多了.这 ...

  8. Java中的线程

    http://hi.baidu.com/ochzqvztdbabcir/item/ab9758f9cfab6a5ac9f337d4 相濡以沫 Java语法总结 - 线程 一 提到线程好像是件很麻烦很复 ...

  9. [译]线程生命周期-理解Java中的线程状态

    线程生命周期-理解Java中的线程状态 在多线程编程环境下,理解线程生命周期和线程状态非常重要. 在上一篇教程中,我们已经学习了如何创建java线程:实现Runnable接口或者成为Thread的子类 ...

随机推荐

  1. Xcode在playground的quick look框中显示对象自定义视图

    对于一般对象,playground中默认的quick look显示已经够用,比如简单的字符串,Int,或简单的自定义Class等等. 不过对于有些情况,我们需要自定义对象在playground中的显示 ...

  2. python 3 dict函数 神奇的参数规则

    >>> dict({1:2},2=3)SyntaxError: keyword can't be an expression>>> dict({1:2},**{2: ...

  3. Hibernate实体映射文件多对多等关系简单应用技巧

    认真开完以后,就能很简单的写出各种关系了 第一步,写注释: <!--xx属性,本类与Yy(类)的多对一 --> <!--xx属性,本类与Yy(类)的一对多 --> <!- ...

  4. Java集合-----java集合框架常见问题

    1什么是Java集合API Java集合框架API是用来表示和操作集合的统一框架,它包含接口.实现类.以及帮助程序员完成一些编程的算法. 简言之,API在上层完成以下几件事: ● 编程更加省力,提高城 ...

  5. Android图片setBackgroundResource和setImageResource的区别

    网上对于这2个的区别大都是如下所示: setImageResource與xml中的src的屬性才是相匹配的,而setBackgroundResource是與xml中的background屬性相匹配 的 ...

  6. Linux下yum安装MySQL yum安装MySQL指定版本

    yum安装MySQL 1. 查看有没有安装过     yum list installed MySQL* (有存在要卸载yum remove MySQL*)     rpm -qa | grep my ...

  7. 基于V4L2摄像头采集图片程序设计

    #ifndef __COMMON_H #define __COMMON_H //该头文件定义的是摄像头在屏幕上显示的宽度和高度 #include<stdio.h> #include< ...

  8. System.getProperty()的用途

     偶尔用到 System.getProperty(),找起来也不方便.这里做下记录备忘: 编写的测试类: public class TestSystemproperty { public stat ...

  9. 安卓中不同APP之间的消息通信

    昨天在腾讯实习生招聘初试面试时面试官问道我关于两个APP之间相互通信的方式,当时自己回道到了contentProvider与BroadcastReceiver.但他接着问还有没有其它的方式,我跟他说可 ...

  10. UNIX网络编程——ICMP报文分析:端口不可达

    ICMP的一个规则是,ICMP差错报文必须包括生成该差错报文的数据报IP首部(包含任何选项),还必须至少包括跟在该IP首部后面的前8个字节(包含源端口和目的端口).在我们的例子中,跟在IP首部后面的前 ...