黑马程序员---多线程

长平狐 发布于 2013/07/01 15:31
阅读 104
收藏 1
------------------------------ android培训java培训、期待与您交流! ------------------------------

多线程是这样一种机制,它允许在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间互相独立。线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线程没有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这使得线程间的通信远较进程简单。

进程:是一个正在执行的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制场景。

线程:就是进程中的一个独立控制单元,线程在控制着进程的执行。一个进程至少有一个线程。

创建线程的两种方式:

方法一:继承 Thread 类,重写方法 run()在创建的 Thread 类的子类中重写 run() , 加入线程所要执行的代码。

public class Test {
	public static void main(String[] args) {
		ThreadTest t = new ThreadTest();
		t.start();
	}
}
//实现Runnable接口
class ThreadTest extends Thread{
	public void run() {
		while(true){
			System.out.println("extends Thread Way");
		}
	}
}

方法二:实现 Runnable接口覆盖run()方法,加入线程所要执行的代码。但是 Runnable 接口并没有任何对线程的支持,还必须创建 Thread 类的实例。

public class Test {
	public static void main(String[] args) {
		ThreadTest2 t = new ThreadTest2();
		Thread thr = new Thread(t);
		thr.start();
	}
}
//实现Runnable接口
class ThreadTest2 implements Runnable{
	public void run() {
		while(true){
			System.out.println("implements Runnable Way");
		}
	}
}

继承方式和实现方式的区别     

继承方式:线程代码存放在Thread子类run方法中

实现方式:线程代码存在接口的子类的run方法中。

实现方式能够实现多继承,避免单继承局限性,在定义线程时,建议使用实现方式。

为什么不直接调用run()而是用start()方法启动线程。

run()方法,只是Thread类的一个方法,它本身并不能开辟线程,跟一般的方法调用是一样的。

start()方法是启动线程的方法,而start()方法启动线程也是调用了run()方法,这也就是为什么要覆盖run()方法的原因:将自定义的代码存储在run方法中,让线程来运行。

线程的生命周期


多线程操作引发线程安全

原因

当多条语句在操作同一个线程共享数据时,一个线程对多条语句执行了一部分,还没执行完,另一个线程参与进来执行,导致共享数据错误。以下代码演示了线程安全问题

public class TicketSaleTest {
	public static void main(String[] args) {
           //启动4个线程,模拟4个售票窗口卖100张票
		Demo5 d1 = new Demo5();
		Thread t1 = new Thread(d1);
		Thread t2 = new Thread(d1);
		Thread t3 = new Thread(d1);
		Thread t4 = new Thread(d1);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

class Demo5 implements Runnable {
	private int x = 100;//100张火车票要出售
	Object obj = new Object();
	public void run() {
		while (true) {
			if (x > 0) {
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			System.out.println(Thread.currentThread().getName()+ "报告:火车票还剩:" + x-- + "张");
			}
		}
	}
}
-------------------运行结果---------------------
……
Thread-0报告:火车票还剩:3张
Thread-2报告:火车票还剩:2张
Thread-3报告:火车票还剩:1张
Thread-0报告:火车票还剩:0张
Thread-2报告:火车票还剩:-1张
Thread-1报告:火车票还剩:-2张
结果中出现了0和负数,出现线程安全问题

解决办法

对多条操作共享数据的语句,只能让一个线程执行完。在执行过程中,其他线程不可以参与执行。java对于多线程的安全问题提供了专业的解决方式,就是同步代码块。形式为:

synchronized(对象){

    需要同步的代码

}

具体代码示例

public class TicketSale2 {
	public static void main(String[] args) {
		Demo2 d1 = new Demo2();
		Thread t1 = new Thread(d1);
		Thread t2 = new Thread(d1);
		Thread t3 = new Thread(d1);
		Thread t4 = new Thread(d1);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

class Demo2 implements Runnable {
	private int x = 100;
	Object obj = new Object();
	public void run() {
		while (true) {
			show();
		}	
	}
	private synchronized void show(){
		if (x > 0) {
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+ "报告:火车票还剩:" + x-- + "张");
		}
	}
}

线程同步

同步函数需要被对象调用,那么函数都有一个所属对象的引用,就是this,如果同步函数被静态修饰后,使用的不是this,静态中方法中也不可以定义this,静态进内存时,内存中没有本类对象,但一定有该类对应的字节码文件对象。类名.class,该对象的

类型是Class。静态同步方法,使用的锁是该方法所在类的字节码文件对象。

public class SyncLockTest2 {
	public static void main(String[] args) {
		Demo4 d4 = new Demo4();
		Thread t1 = new Thread(d4);
		Thread t2 = new Thread(d4);
		t1.start();
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		d4.flag = false;
		t2.start();
	}
}

class Demo4 implements Runnable {
	//x变成静态的
	private static int x = 100;
	Object obj = new Object();
	boolean flag = true;
	public void run() {
		if (flag) {
			while (true) {
				//synchronized同步块()中的对象变成了Demo4的字节码对象
				synchronized (Demo4.class) {
					if (x > 0) {
						try {
							Thread.sleep(10);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						System.out.println(Thread.currentThread().getName()+ "code Block------" + x--);
					}
				}
			}
		} else {
			while (true) {
				show();
			}
		}
	}
	//show()方法里面增加了static修饰关键字
	private static synchronized void show() {
		if(x > 0) {
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+ "show method--------" + x--);
		}
	}

}

/*
单例模式(懒汉式),可以实现延迟实例加载,但是当多线程访问时有安全隐患,采用如下方式可以避免安全隐患和提高效率
*/

class Single{
	   private static Single s = null;
	   private  Single(){}
	   public static Single getInstance(){
	       if(s==null){
	           synchronized(Single.class){
	          if(s==null){
	             s = new Single();
	         }
	     }
	       }
	         return s;
	   }
	}

死锁:系统中两个或者多个进程无限期 地等待永远不会发生的条件,系统处于停滞状态。 
产生死锁的原因主要是: 
(1) 系统资源不足。 
(2) 进程运行推进的顺序不合适。 
(3) 资源分配不当等。 

产生死锁的四个必要条件: 
(1) 互斥条件:一个资源每次只能被一个进程使用。 
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。 
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。 

一个典型的死锁:

public class DeadLockTest {
	public static void main(String[] args) {
		Thread t1 = new Thread(new TestLock(true));
		Thread t2 = new Thread(new TestLock(false));
		t1.start();
		t2.start();
	}
}
class TestLock implements Runnable {
	boolean flag;
	TestLock(boolean flag) {
		this.flag = flag;
	}
	public void run() {
		if (flag) {
			synchronized (MyLock.lockA) {
				System.out.println("if locka");
				synchronized (MyLock.lockB) {
					System.out.println("if lockb");
				}
			}
		} else {
			synchronized (MyLock.lockB) {
				System.out.println("else lockb");
				synchronized (MyLock.lockA) {
					System.out.println("else locka");
				}
			}
		}
	}
}
class MyLock {
	static Object lockA = new Object();
	static Object lockB = new Object();



------------------------------ android培训java培训、期待与您交流! ------------------------------

原文链接:http://blog.csdn.net/voiceofnet/article/details/7277788
加载中
返回顶部
顶部