当前位置:首页 » 编程语言

【Java笔记】——如何理解线程

2015-09-11 21:33 本站整理 浏览(180)

上篇文章说道在Java中,“流”是抽象的概念,不容易理解。而所谓的进程和线程,同样也是看不到摸不着的,同样属于抽象概念。但是把进程和线程形象化之后,就会发现,其实两者有很大的区别。

简单理解进程和线程,现在的操作系统都是多任务操作系统,可以同时运行很多应用程序,进程就是内存中一个正在运行的应用程序,它有自己独立的内存空间,而且可以启动多条线程。比如现在有一个支持多用户登录的系统,系统启动是一个进程,而多个人登录就是多个线程,这样理解起来就方便多了。

概念

线程是一个程序内部的顺序控制流,Java中的线程是通过java.lang.Thread类来实现的。

创建和启动

线程的创建可以有两种方式,第一种是定义的线程类实现Runnable接口
<span style="font-family:KaiTi_GB2312;font-size:18px;"><span style="font-family:KaiTi_GB2312;font-size:18px;"><span style="font-family:KaiTi_GB2312;font-size:18px;"><span style="font-family:KaiTi_GB2312;font-size:18px;">public class TestThread{
	public static void main(String args[]){
		Runner r=new Runner();
		Thread t=new Thread(r);
		t.start();    //启动线程
		
		for(int i=0;i<100;i++){
			System.out.println("Main Thread:---------"+i);	
		}	
	}	
}

class Runner implements Runnable{
	public void run(){
		for(int i=0;i<100;i++){
			System.out.println("Runner:"+i);	
		}	
	}	
}</span></span></span></span>

第二种是定义一个Thread的子类,并重写其run()方法

<span style="font-family:KaiTi_GB2312;font-size:18px;"><span style="font-family:KaiTi_GB2312;font-size:18px;"><span style="font-family:KaiTi_GB2312;font-size:18px;"><span style="font-family:KaiTi_GB2312;font-size:18px;">public class TestThread{
	public static void main(String args[]){
		Runner r=new Runner();
		r.start();      //启动线程
		
		for(int i=0;i<100;i++){
			System.out.println("Main Thread:---------"+i);	
		}	
	}	
}

class Runner extends Thread{
	public void run(){
		for(int i=0;i<100;i++){
			System.out.println("Runner:"+i);	
		}	
	}	
}</span></span></span></span>

线程同步

什么是线程同步?在实际应用中,会有这样的情况,两个线程同时对相同数据进行操作,这样就会产生问题,会导致两个线程都的不到自己满意的返回数据,解决这个问题的方法就是线程同步。

线程同步就是给数据加锁,在一个线程访问当前对象时,给当前对象加锁,执行完成后解锁,然后另一个线程才能进行访问。不过加锁之后,并不能解决一切问题,因为这样会引起“死锁”。

死锁是当多个进程需要同时访问多个对象时会引起的问题,下面看一个小例子:

<span style="font-family:KaiTi_GB2312;font-size:18px;"><span style="font-family:KaiTi_GB2312;font-size:18px;"><span style="font-family:KaiTi_GB2312;font-size:18px;"><span style="font-family:KaiTi_GB2312;font-size:18px;">public class TestDeadLock implements Runnable {
	public int flag = 1;
	static Object o1 = new Object(), o2 = new Object();
	public void run() {
System.out.println("flag=" + flag);
		if(flag == 1) {     //当flag=1时,对象o1加锁,只允许一个线程访问
			synchronized(o1) {
				try {
					Thread.sleep(500);   //线程睡眠半秒钟
				} catch (Exception e) {
					e.printStackTrace();
				}
				synchronized(o2) {   //访问对象o2,并对o2加锁
					System.out.println("1");	
				}
			}
		}
		if(flag == 0) {    //当flag=0时,对象o2加锁,只允许一个线程访问
			synchronized(o2) {
				try {
					Thread.sleep(500);    //线程睡眠半秒钟
				} catch (Exception e) {
					e.printStackTrace();
				}
				synchronized(o1) {    //访问对象o1,并对o1加锁
					System.out.println("0");
				}
			}
		}
	}	
	
	public static void main(String[] args) {
		TestDeadLock td1 = new TestDeadLock(); 
		TestDeadLock td2 = new TestDeadLock();   
		td1.flag = 1;   
		td2.flag = 0;   
		Thread t1 = new Thread(td1);   //线程1,且flag为1
		Thread t2 = new Thread(td2);   //线程2,且flag为0
		t1.start();    //线程1启动,此时flag为1,访问对象o1并加锁
		t2.start();    //线程2启动,此时flag为0,访问对象o2并加锁
		
	}
}</span></span></span></span>
线程1和线程2都需要对象o1和o2才能完成,如代码中所示,线程1的flag为1,所以此时正访问对象o1,并且锁定了对象o1,而它再需要访问对象o2就能完成任务。同样线程2也是这样,它的flag为0,此时正访问对象o2,并且锁定了对象o2,再需要访问对象o1就能完成任务。而此时的问题是,两者都没有完成任务,所以都不释放当前锁定的对象,而且都需要对方锁定的对象,这是就产生了死锁。

总结

线程是轻量级的进程,如果一个进程只有一个线程,就好像一个饭店只有一个厨师,在一定情况下是不符合实际的,这也就有了多线程。但是,多线程中的死锁问题是很容易出现的,原因就在于锁的设置不合理,只要在项目中合理的设置锁的位置,死锁问题还是可以避免的。