上篇文章说道在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>
<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就能完成任务。而此时的问题是,两者都没有完成任务,所以都不释放当前锁定的对象,而且都需要对方锁定的对象,这是就产生了死锁。
线程是轻量级的进程,如果一个进程只有一个线程,就好像一个饭店只有一个厨师,在一定情况下是不符合实际的,这也就有了多线程。但是,多线程中的死锁问题是很容易出现的,原因就在于锁的设置不合理,只要在项目中合理的设置锁的位置,死锁问题还是可以避免的。