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

来源:互联网 发布:网络用语打call的意思 编辑:程序博客网 时间:2024/06/14 04:16

    上篇文章说道在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为1Thread t2 = new Thread(td2);   //线程2,且flag为0t1.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就能完成任务。而此时的问题是,两者都没有完成任务,所以都不释放当前锁定的对象,而且都需要对方锁定的对象,这是就产生了死锁。


总结

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



0 0