黑马程序员--多线程

 

---------------------- ASP.Net+Android+IOS开发.NET培训、期待与您交流! ----------------------

 一、多线程的概念

线程指进程中的一个执行场景,也就是执行流程,那么进程和线程有什么区别呢?

  • 每个进程是一个应用程序,都有独立的内存空间
  • 同一个进程中的线程共享其进程中的内存和资源

(共享的内存是堆内存和方法区内存,栈内存不共享,每个线程有自己的)

 

1.什么是进程?

 一个进程对应一个应用程序

例如:在windows操作系统中启动word就表示启动了一个进程,在java的开发环境下启动了jvm,就表示启动了一个进程。现在的计算机都是支持多进程的,在同一个操作系统中,可以同时启动多个进程。

2.多进程有什么作用?

单进程计算机只能做一件事情。

玩电脑:一边玩游戏,一边听音乐

对于单核计算机来讲,在同一个点上,游戏进程和音乐进程是同时在运行吗?不是。

因为计算机的CPU只能在某一时间上点做一件事。

那为什么人们看到的是同时在进行呢?那是因为计算机的处理速度非常的快,两个进程之间频繁的不断切换,已经超出了人类的感觉。人类感觉游戏和音乐同时进行。

多进程的作用不是提高执行速度,而是提高CPU的使用率。

进程和进程之间的内存是独立的。

3.什么事线程?

 线程是一个进程中的执行场景,一个进程可以启动多个线程。

4.多线程有什么作用?

多线程不是为了提高执行速度,而是提高应用程序的使用率。

线程和线程共享"堆内存和方法区内存",栈内存是独立的,一个线程一个栈。


二、创建多线程

1、继承Thread类

1>定义类继承Thread类

2>重写Thread类的run()方法

目的:将自定义的代码存储于run方法中,让线程执行

3>调用Thread类的start()方法

该方法两个作用:启动线程,调用run()方法

class ThreadDemo extends Thread
{
	public void run(){
	    for(int x = 0; x < 60;x++){
		    System.out.println("DemoThread run--->"+x);
	    }
	}
}

public class Demo
{
	public static void main(String args[]){
		ThreadDemo t = new ThreadDemo();
		t.start();
		
		for(int x = 0; x < 60;x++){
		    System.out.println("Hello World--->"+x);
		    
		}
	}
}
发现运行结果每一次都不同
因为多个线程都获取CPU的执行权,CPU执行到谁,谁就运行。
明确一点,在某一个时刻,只能有一个程序在运行(多核除外)
CPU在做着快速的切换,以达到看上去是同时运行的效果。
我们可以形象把多线程的运行行为在互相抢夺CPU的执行权
这就是多线程的一个特性:随机性、谁抢到谁执行,至于执行多长,CPU说的算


2.声明实现 Runnable 接口的类

1>定义类实现Runnable接口

2>覆盖Runnable接口的run方法

3>将Runnable接口的子类对象作为实际参数传递给Thread类的构造方法

4>调用Thread类的start方法开启线程调用Runnable接口子类的run方法

class ThreadDemo implements Runnable  //实现 Runnable 接口
{
	public void run(){  //重写run()方法
	    for(int x = 0; x < 60;x++){
		    System.out.println("DemoThread run--->"+x);
	    }
	}
}

public class Demo
{
	public static void main(String args[]){
		ThreadDemo td = new ThreadDemo();
		Thread t = new Thread(td);
		t.start();
		
		for(int x = 0; x < 60;x++){
		    System.out.println("Hello World--->"+x);
		    
		}
	}
}
其实 Thread 中的 run 方法调用的是 Runnable 接口的 run 方法。不知道大家发现没有, Thread Runnable 都实现了 run 方法,这种操作模式其实就是代理模式

Thread和Runnable的区别:

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
  * @author Rollen-Holt 继承Thread类,不能资源共享
  * */
class hello extends Thread {
     public void run() {
         for ( int i = 0 ; i < 7 ; i++) {
             if (count > 0 ) {
                 System.out.println( "count= " + count--);
             }
         }
     }
 
     public static void main(String[] args) {
         hello h1 = new hello();
         hello h2 = new hello();
         hello h3 = new hello();
         h1.start();
         h2.start();
         h3.start();
     }
 
     private int count = 5 ;
}

 

【运行结果】:

count= 5

count= 4

count= 3

count= 2

count= 1

count= 5

count= 4

count= 3

count= 2

count= 1

count= 5

count= 4

count= 3

count= 2

count= 1

大家可以想象,如果这个是一个买票系统的话,如果count表示的是车票的数量的话,说明并没有实现资源的共享。

我们换为Runnable接口

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class MyThread implements Runnable{
 
     private int ticket = 5 //5张票
 
     public void run() {
         for ( int i= 0 ; i<= 20 ; i++) {
             if ( this .ticket > 0 ) {
                 System.out.println(Thread.currentThread().getName()+ "正在卖票" + this .ticket--);
             }
         }
     }
}
public class lzwCode {
     
     public static void main(String [] args) {
         MyThread my = new MyThread();
         new Thread(my, "1号窗口" ).start();
         new Thread(my, "2号窗口" ).start();
         new Thread(my, "3号窗口" ).start();
     }
}

 

  

 

 

【运行结果】:

count= 5

count= 4

count= 3

count= 2

count= 1

 

总结一下吧:

实现Runnable接口比继承Thread类所具有的优势:

1):适合多个相同的程序代码的线程去处理同一个资源

2):可以避免java中的单继承的限制

3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。

 

所以,本人建议大家劲量实现接口。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
  * @author Rollen-Holt
  * 取得线程的名称
  * */
class hello implements Runnable {
     public void run() {
         for ( int i = 0 ; i < 3 ; i++) {
             System.out.println(Thread.currentThread().getName());
         }
     }
 
     public static void main(String[] args) {
         hello he = new hello();
         new Thread(he, "A" ).start();
         new Thread(he, "B" ).start();
         new Thread(he).start();
     }
}

【运行结果】:

A

A

A

B

B

B

Thread-0

Thread-0

Thread-0

说明如果我们没有指定名字的话,系统自动提供名字。

提醒一下大家:main方法其实也是一个线程。在java中所以的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。



3.run()与start()方法的特点

为什么要覆盖run方法呢?
Thread类用于描述线程
该类定义了一个功能,用于存储要运行的代码,该存储功能就是run方法,也就是说Thread类中run(),用于存储线程要运行的代码
例如下面代码,运行结果又是啥样的呢?

class ThreadDemo extends Thread
{
	public void run(){
	    for(int x = 0; x < 60;x++){
		    System.out.println("DemoThread run--->"+x);
	    }
	}
}

public class Demo
{
	public static void main(String args[]){
		ThreadDemo t = new ThreadDemo();
		//t.start();
		t.run();//仅仅是对象调用方法,而线程创建了,并没有运行
		
		for(int x = 0; x < 60;x++){
		    System.out.println("Hello World--->"+x);
		    
		}
	}
}

三、线程运行状态

四、获取线程的名称
1.getName();
原来线程都有自己默认的名称
Thread-编号,改编号0开始

2.Thread.currentThread.getName();

五、卖票小程序
public class SaleTicket
{
	public static void main(String[] args) 
	{
		
		Ticket t = new Ticket();
		new Thread(t,"窗口1").start();
		new Thread(t,"窗口2").start();
		new Thread(t,"窗口3").start();
		new Thread(t,"窗口4").start();
		new Thread(t,"窗口5").start();

	}
}

class Ticket implements Runnable
{
	private int ticket = 20;
	
	public void run(){
		while(true){
			if(ticket>0){
				System.out.println(Thread.currentThread().getName()+"正在卖"+ticket--+"号票");
			}
		}
	}
}
但上面的代码有问题吗?会出现,例如在原有的代码改成下面的代码,会打印出0、-1、-2、-3号票
public class SaleTicket
{
	public static void main(String[] args) 
	{
		
		Ticket t = new Ticket();
		new Thread(t,"窗口1").start();
		new Thread(t,"窗口2").start();
		new Thread(t,"窗口3").start();
		new Thread(t,"窗口4").start();
		new Thread(t,"窗口5").start();

	}
}

class Ticket implements Runnable
{
	private int ticket = 20;
	
	public void run(){
		while(true){
			if(ticket>0){
				try{
					Thread.sleep(10);
				}catch(InterruptedException e){
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"正在卖"+ticket--+"号票");
			}
		}
	}
}
执行结果:

多线程运行期间出现了安全问题

问题的原因:

当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误

解决办法:对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。

java对于多线程的安全问题提供了专业的解决方式,就是同步代码块

synchronized(对象){

需要被同步的代码

}

public class SaleTicket
{
	public static void main(String[] args) 
	{
		
		Ticket t = new Ticket();
		new Thread(t,"窗口1").start();
		new Thread(t,"窗口2").start();
		new Thread(t,"窗口3").start();
		new Thread(t,"窗口4").start();
		new Thread(t,"窗口5").start();

	}
}

class Ticket implements Runnable
{
	private int ticket = 20;
	Object obj = new Object();
	public void run(){
		while(true){
			synchronized(obj){  //同步代码块,给对象加锁
				if(ticket>0){
					try{
						Thread.sleep(10);
					}catch(InterruptedException e){
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"正在卖"+ticket--+"号票");
				}
			}
		}
	}
}
执行结果:


 对象如同锁,持有锁的线程可以在同步中执行。没有持有锁的线程即使获取CPU的执行权,也进不去,因为没有获取锁

同步的前提:

1.必须两个或两个以上的线程

2.必须是多个线程使用同一个锁 

必须保证同步中只能有一个线程在执行

好处:解决了多线程的安全问题

弊端:每次都要判断锁,消耗了资源


同步函数:

函数需要被对象调用。那么函数都有一个所属对象引用。就是this。
所以同步函数使用的锁是this。

使用同步函数修改卖票代码:

[java]  view plain copy print ?
  1. package it.haha.Throwable;  
  2.   
  3. public class Thread_1 {  
  4.   
  5.     public static void main(String[] args) {  
  6.         Ticket t = new Ticket();  
  7.         Thread t1 = new Thread(t);  
  8.         Thread t2 = new Thread(t);  
  9.         Thread t3 = new Thread(t);  
  10.         Thread t4 = new Thread(t);  
  11.         t1.start();  
  12.         t2.start();  
  13.         t3.start();  
  14.         t4.start();       
  15.     }  
  16.   
  17. }  
  18.   
  19. class Ticket implements Runnable  
  20. {  
  21.     private int tick =500;  
  22.     private boolean b = true//定义一个变量用来控制while循环  
  23.     public void run() //不能将同步函数定义在run方法上,否则将一个线程执行时别的线程进不来,跟单线程没有区别了。  
  24.     {  
  25.         while(b)  
  26.         {  
  27.             show(); //调用同步方法。  
  28.         }  
  29.     }  
  30.     public synchronized void show()  
  31.     {  
  32.             try  
  33.             {  
  34.                     if(tick>0)//将需要操作共享数据的代码写到同步代码块中。  
  35.                     {  
  36.                         Thread.sleep(2); //让线程睡眠两毫秒,为了让安全问题更容易显示  
  37.                         System.out.println(Thread.currentThread().getName()+"还剩"+tick--+"票");//打印余票  
  38.                     }  
  39.                     else  
  40.                         b = false//当tick为0 时将b改为false 终止循环      
  41.             }  
  42.             catch(Exception e)  
  43.             {  
  44.                 e.printStackTrace();  
  45.             }  
  46.     }  
  47. }  

六、多线程的单例设计模式

/*
饿汉式
*/

class Singleton
{
	private static Singleton s = new Singleton();
	private Singleton(){}
	public static Singleton getInstance(){
		return s;
	}
}

/*
懒汉式
*/
class Singleton
{
	private static Singleton s = null;
	private Singleton(){}
	public static Singleton getInstance(){
		//利用多重判断,实现线程安全并且效率高,能有多个线程访问
		if(s == null){
			synchronized(Singleton.class){
				if(s==null){
					s = new Singleton();
				}
			}
		}
		return s;
	}
}
 
七、线程间通信例子
/**
线程间的通信
**/

//公共资源
class Res
{
	public String name;
	public String sex;

}
class InputThread implements Runnable
{
	//初始化资源
	private Res r;
	public InputThread(Res r){
		this.r = r;
	}

	public void run(){
		int x = 0;
		while(true){
			synchronized(r){
				if(x==0){
					r.name = "Mike";
					r.sex = "man";
				}
				else{
					r.name = "丽丽";
					r.sex = "女";
				}
				x = (x+1) % 2;
			}
		}
	}

}

class OutputThread implements Runnable
{
	//初始化资源
	private Res r;
	public OutputThread(Res r){
		this.r = r;
	}
	public void run(){
		while(true){
			synchronized(r){
				System.out.println(r.name+"......"+r.sex);
		    }
		}
	}

}

public class ThreadDemo4
{
	public static void main(String args[]){
		//实例化资源
		Res r = new Res();

		InputThread it = new InputThread(r);
		OutputThread ot = new OutputThread(r);

		new Thread(it).start();
		new Thread(ot).start();
	}

}
执行结果:
 
八、等待唤醒机制
/**
线程间的通信
**/

//公共资源
class Res
{
	public String name;
	public String sex;
	public boolean flag = false;  //标记

}
class InputThread implements Runnable
{
	//初始化资源
	private Res r;
	public InputThread(Res r){
		this.r = r;
	}

	public void run(){
		int x = 0;
		while(true){
			synchronized(r){
				//如果有资源,则等待
				if(r.flag){
					try{
						wait();
					}catch(InterruptedException e){
						e.printStackTrace();
					}
				}
				if(x==0){
					r.name = "Mike";
					r.sex = "man";
				}
				else{
					r.name = "丽丽";
					r.sex = "女";
				}
				x = (x+1) % 2;
				//资源添加完,则把标签改为true,让其它知道里面有资源
				r.flag = true;
				//唤醒其它线程来取资源
				notify();
			}
		}
	}

}

class OutputThread implements Runnable
{
	//初始化资源
	private Res r;
	public OutputThread(Res r){
		this.r = r;
	}
	public void run(){
		while(true){
			synchronized(r){
				//如果没有资源,则等待
				if(!r.flag){
					try{
						wait();
					}catch(InterruptedException e){
						e.printStackTrace();
					}
				}
				System.out.println(r.name+"......"+r.sex);
				//取完资源后,则把标签改为false
				r.flag = false;
				//唤醒其它线程,添加资源
				notify();
		    }
		}
	}

}

public class ThreadDemo4
{
	public static void main(String args[]){
		//实例化资源
		Res r = new Res();

		InputThread it = new InputThread(r);
		OutputThread ot = new OutputThread(r);

		new Thread(it).start();
		new Thread(ot).start();
	}

}
 
九、生产者消费者例子
import java.util.concurrent.*;


public class ThreadBlockingQueue
{
	public static void main(String[] args){
		Resource r = new Resource();

		Producer p = new Producer(r);

		Consumer c = new Consumer(r);

		new Thread(p).start();

		new Thread(c).start();
	}
}

//公共资源
class Resource 
{
	//容量为10个资源
	BlockingQueue
     
     
      
       bq = new ArrayBlockingQueue
      
      
       
       (10);

	//生产资源
	public void produce() throws InterruptedException{
			bq.put("An Apple");
	}

	//消费资源
	public String consumer() throws InterruptedException{
			return bq.take();
		
	}
}

class Producer implements Runnable
{
	private Resource r;
	public Producer(Resource r){
		this.r = r;
	}
	public void run(){
		try
		{
			while(true){
				System.out.println("生产者正在准备生产资源:"+System.currentTimeMillis());
				r.produce();
				System.out.println("生产者生产资源完毕"+System.currentTimeMillis());
				Thread.sleep(300);
			}
		}
		catch (InterruptedException e)
		{
			e.printStackTrace();
		}

	}
}

class Consumer implements Runnable
{
	private Resource r;
	public Consumer(Resource r){
		this.r = r;
	}

	public void run(){
		try
		{
			while(true){
				System.out.println("消费者正在消费资源:"+System.currentTimeMillis());
				r.consumer();
				System.out.println("消费者消费资源完毕:"+System.currentTimeMillis());
				Thread.sleep(1000);
			}
		}
		catch (InterruptedException e)
		{
			e.printStackTrace();
		}

	}
}

      
      
     
     
 
十、线程的几个方法
1>停止线程
 
如果您想要停止一个线程的执行,当您查看API时,您会发现Thread的stop()方法已经被标示为 "deprecated",使用这个方法来停止一个线程是不被建议的。
 
如果您想要停止一个线程,您最好自行实作。
 
一个线程要进入Dead状态,就是执行完run()方法,简单的说,如果您想要停止一个线程的执行,就要提供一个方式让线程可以执行完run(),而这也是您自行实作线程停止的基本概念。
 
例如,如果线程的run()方法中执行的是一个重复执行的循环,您可以提供一个flag来控制循环是否执行,借此让循环有可能终止、线程可以离开 run()方法以终止线程:
 
public class SomeThread implements Runnable {
private boolean isContinue = true;
 
public void terminate() {
isContinue = false;
}
 
public void run() {
while(isContinue) {
// ... some statements
}
}
}
 
 
如果线程因为执行sleep()或是wait()而进入Not Runnable状态,而您想要停止它,您可以使用interrupt(),而程式会丢出InterruptedException例外,因而使得执行绪离开run()方法,例如:
package src.bean;
 
public class SomeThread {
 
public static void main(String[] args)
{
Thread thread=new Thread(new Runnable(){
 
public void run() {
// TODO Auto-generated method stub
System.out.println("go to sleep");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println("i am interrupted!");
}
}
 
});
thread.start();
thread.interrupt();
}
}
执行结果为:
 
go to sleep
i am interrupted!
 
 
如果程序因为I/O而停滞,进入Not Runnable状态,基本上您必须等待I/O完成才能离开Not Runnable,您无法使用interrupt()来使得执行绪离开run()方法,您要提供替代的方法,基本上的概念也是引发一个例外,而这个例外要如何引发,要看您所使用的I/O而定,例如您使用readLine()在等待网路上的一个信息,此时线程进入Not Runnable直到读到一个信息,您要让它离开run()的方法就是使用close()关闭它的串流,这时会引发一个IOException例外而使得线程离开run()方法,例如:
 
public class Client implements Runnable {
private Socket skt;
// .....
 
public void terminate() {
skt.close();
}
 
public void run() {
// .....
 
try {
BufferedReader buf = new BufferedReader(
new InputStreamReader(skt.getInputStream()));
 
// 读取客户端讯息
// 执行readLine()会进入Not runnable状态
// 直到读到客户端讯息
while((userMessage = buf.readLine()) != null) {
// ....
}
}
catch(IOException e) {
System.out.println("执行绪被终止.......");
}
}
}
 
 
上面这个程序是个简单的架构示范,实际的设计必须视您的程序功能与I/O类型而定。
 
除了stop()之外,suspend()、resume()方法也被标示为"deprecated",这些方法如果您要达成相同的功能,您都必须自行实作.

2>Java的Thread.setDaemon


Thread.setDaemon的用法,经过学习以后了解:
1. setDaemon需要在start方法调用之前使用
2. 线程划分为用户线程和后台(daemon)进程,setDaemon将线程设置为后台进程
3. 如果jvm中都是后台进程,当前jvm将exit。(随之而来的,所有的一切烟消云散,包括后台线程啦)
4. 主线程结束后,
      1) 用户线程将会继续运行
      2) 如果没有用户线程,都是后台进程的话,那么jvm结束
 
另外:
setDaemon方法把java的线程设置为守护线程,此方法的调用必须在线程启动之前执行。只有在当前jvm中所有的线程都为守护线程时,jvm才会退出。
如果创建的线程没有显示调用此方法,这默认为用户线程。
实例如下:
package com.jack.mySample;   
  
import java.io.IOException;   
  
public class TestThread extends Thread {   
       
    public TestThread() {   
    }   
    /** *//**  
     * 线程的run方法,它将和其他线程同时运行  
     */  
    public void run(){   
        for(int i = 1; i <= 100; i++){   
            try{   
                Thread.sleep(100);   
                   
            } catch (InterruptedException ex){   
                ex.printStackTrace();   
            }   
            System.out.println(i);   
        }   
    }   
    public static void main(String [] args){   
        TestThread test = new TestThread();   
        // 如果不设置daemon,那么线程将输出100后才结束   
        test.setDaemon(true);   
        test.start();   
        System.out.println("isDaemon = " + test.isDaemon());   
        try {   
            System.in.read(); // 接受输入,使程序在此停顿,一旦接收到用户输入,main线程结束,守护线程自动结束   
        } catch (IOException ex) {   
            ex.printStackTrace();   
        }   
    }   

 
补充说明:
定义:守护线程--也称“服务线程”,在没有用户线程可服务时会自动离开。
优先级:守护线程的优先级比较低,用于为系统中的其它对象和线程提供服务。
设置:通过setDaemon(true)来设置线程为“守护线程”;将一个用户线程设置为
      守护线程的方式是在 线程对象创建 之前 用线程对象的setDaemon方法。
example: 垃圾回收线程就是一个经典的守护线程,当我们的程序中不再有任何运行的
      Thread,程序就不会再产生垃圾,垃圾回收器也就无事可做,所以当垃圾回收线程是
      JVM上仅剩的线程时,垃圾回收线程会自动离开。它始终在低级别的状态中运行,用于
      实时监控和管理系统中的可回收资源。
生命周期:守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且
      周期性地执行某种任务或等待处理某些发生的事件。也就是
      说守护线程不依赖于终端,但是依赖于系统,与系统“同生共死”。那Java的守护线程是
      什么样子的呢。当JVM中所有的线程都是守护线程的时候,JVM就可以退出了;如果还有一个
      或以上的非守护线程则JVM不会退出。
 
 
初次碰到这个方法,就被这个怪里怪气的拼写单词懵了下,查API又觉得理解得不深刻,那还是查下Daemon这个词有什么意思,什么词源吧。Daemon 中的 ae 为一个音,重音在 /di:/ 上,好象是指希腊守护神的意思吧。在计算机专业英语中是守护线程的意思。原以为如果一个线程被光荣地定义为守护线程,一定会直到进程运行到最后一刻,但真错了,如果一个线程是守护线程,那么,主线程运行结束时,如果没有任何非守护线程在运行,守护线程就会自尽了。 setDaemon方法是Thread中的方法,默认为false状态,将该线程标记为守护线程或用户线程,该方法必须在启动线程前调用,具有最低的优先级,让系统资源优先调用其他线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。


如下边这个小程序如示:
import java.io.IOException;
class TestMain4 extends Thread {
   public void run() {            //永真循环线程
       for(int i=0;;i++){
           try {
               Thread.sleep(1000);
           } catch (InterruptedException ex) {   }
           System.out.println(i);
       }
   }


   public static void main(String [] args){
      TestMain4 test = new TestMain4();
      test.setDaemon(true);    //调试时可以设置为false,那么这个程序是个死循环,没有退出条件。设置为true,即可主线程结束,test线程也结束。
       test.start();
       System.out.println("isDaemon = " + test.isDaemon());
       try {
           System.in.read();   // 接受输入,使程序在此停顿,一旦接收到用户输入,main线程结束,守护线程自动结束
       } catch (IOException ex) {}
   }
}

3>  join() 方法主要是让调用该方法的thread完成run方法里面的东西后, 再执行join()方法后面的代码

4>Thread.yield()方法作用是:暂停当前正在执行的线程对象,并执行其他线程。
 
yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。
 
结论:yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。

 
 
 十一、java的yield()、sleep()、wait()方法区别详解
1、sleep()
使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。也就是说如果有synchronized同步快,其他线程仍然不能访问共享数据。注意该方法要捕捉异常。
例如有两个线程同时执行(没有synchronized)一个线程优先级为MAX_PRIORITY,另一个为MIN_PRIORITY,如果没有Sleep()方法,只有高优先级的线程执行完毕后,低优先级的线程才能够执行;但是高优先级的线程sleep(500)后,低优先级就有机会执行了。
总之,sleep()可以使低优先级的线程得到执行的机会,当然也可以让同优先级、高优先级的线程有执行的机会。


2、join()
join()方法使调用该方法的线程在此之前执行完毕,也就是等待该方法的线程执行完毕后再往下继续执行。注意该方法也需要捕捉异常。
 
3、yield()
该方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会。
 
4、wait()和notify()、notifyAll()
 
这三个方法用于协调多个线程对共享数据的存取,所以必须在synchronized语句块内使用。synchronized关键字用于保护共享数据,阻止其他线程对共享数据的存取,但是这样程序的流程就很不灵活了,如何才能在当前线程还没退出synchronized数据块时让其他线程也有机会访问共享数据呢?此时就用这三个方法来灵活控制。
wait()方法使当前线程暂停执行并释放对象锁标示,让其他线程可以进入synchronized数据块,当前线程被放入对象等待池中。当调用notify()方法后,将从对象的等待池中移走一个任意的线程并放到锁标志等待池中,只有锁标志等待池中线程能够获取锁标志;如果锁标志等待池中没有线程,则notify()不起作用。
notifyAll()则从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中。
注意 这三个方法都是java.lang.Object的方法。




二、run和start()
把需要处理的代码放到run()方法中,start()方法启动线程将自动调用run()方法,这个由java的内存机制规定的。并且run()方法必需是public访问权限,返回值类型为void。


三、关键字synchronized
该关键字用于保护共享数据,当然前提条件是要分清哪些数据是共享数据。每个对象都有一个锁标志,当一个线程访问到该对象,被Synchronized修饰的数据将被"上锁",阻止其他线程访问。当前线程访问完这部分数据后释放锁标志,其他线程就可以访问了。




四、wait()和notify(),notifyAll()是Object类的方法,sleep()和yield()是Thread类的方法。
(1)、常用的wait方法有wait()和wait(long timeout);
void wait() 在其他线程调用此对象的 notify() 方法或者 notifyAll()方法前,导致当前线程等待。
void wait(long timeout)在其他线程调用此对象的notify() 方法 或者 notifyAll()方法,或者超过指定的时间量前,导致当前线程等待。
wait()后,线程会释放掉它所占有的“锁标志”,从而使线程所在对象中的其他shnchronized数据可被别的线程使用。


wait()h和notify()因为会对对象的“锁标志”进行操作,所以他们必需在Synchronized函数或者 synchronized block 中进行调用。如果在non-synchronized 函数或 non-synchronized block 中进行调用,虽然能编译通过,但在运行时会发生IllegalMonitorStateException的异常。。


(2)、Thread.sleep(long millis)必须带有一个时间参数。
sleep(long)使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会被执行;
sleep(long)可使优先级低的线程得到执行的机会,当然也可以让同优先级的线程有执行的机会;
sleep(long)是不会释放锁标志的。


(3)、yield()没有参数
sleep 方法使当前运行中的线程睡眠一段时间,进入不可以运行状态,这段时间的长短是由程序设定的,yield方法使当前线程让出CPU占有权,但让出的时间是不可设定的。
yield()也不会释放锁标志。
实际上,yield()方法对应了如下操作;先检测当前是否有相同优先级的线程处于同可运行状态,如有,则把CPU的占有权交给次线程,否则继续运行原来的线程,所以yield()方法称为“退让”,它把运行机会让给了同等级的其他线程。


sleep 方法允许较低优先级的线程获得运行机会,但yield()方法执行时,当前线程仍处在可运行状态,所以不可能让出较低优先级的线程此时获取CPU占有权。在一个运行系统中,如果较高优先级的线程没有调用sleep方法,也没有受到I/O阻塞,那么较低优先级线程只能等待所有较高优先级的线程运行结束,方可有机会运行。


yield()只是使当前线程重新回到可执行状态,所有执行yield()的线程有可能在进入到可执行状态后马上又被执行,所以yield()方法只能使同优先级的线程有执行的机会。

---------------------- ASP.Net+Android+IOS开发.NET培训、期待与您交流! ----------------------

详细请查看:http://edu.csdn.net

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值