Java多线程学习总结

在开始这个话题之前,我们先聊一下线程。线程的目的就是为了让一些程序片段能够“同时”进行,至少是视觉上。因为实际上,这些程序片段是“交替”执行的。如图1

在途中,线程A和线程B轮流交替地占有CPU的处理时间。也许有人说,CPU总的计算量并没有降低啊,确实如此,还可以切换线程,还增加了一点点的计算量。如果现在有一个很长很复杂很费时的计算任务,和一个精彩电影的播放任务,那我们当然愿意选择让这两个不太相干的任务同时并发执行。由此可见线程的目的是用一些调度算法,使电脑用户不会“等待”的不耐烦。因为绝大多数的用户都希望计算机体贴周到,富有人性化,提供更好的服务。事实上,我们在计算机上做的很多革新,就是为了如此。

       Java是支持线程的,不像C++,还需要操作系统的配合才行,当然java最终还是要用到操作系统底层的支持,才可以拥有多线程编程的能力,呵呵。Java实现线程非常简单。一个是继承Thread,一个是实现Runnable接口。我突然想起我毕业时面试时经常被问这样的问题,那时只不过是机械式的回答,里面有许多的道理倒没有现在认识深刻。我们需要并发的代码写在run方法里面就OK了,呵呵。至于java底层是如何压栈入栈,保护程序执行现场的,如何恢复现场的,我们就不必深究了,因为我们不是API级的程序员,我们需要的是学习别人的成果,迅速copy,快速开发,不然怎么超英赶美啊?我们的目的是为了图2中的“顺序执行”变成图1中的“并发执行”。其中图1中的“时间片”的划分是随机的。

 

Java的线程执行,也是非常简单的,new 一个线程,然后startOK了。

关于Thread这个类,有时候容易混淆。打个比方说,使用这个类可以类比成英语里面的语法“Somebody do Something”。举个例子:

SomeBody类:

  

package wjj2;

public class SomeBody {
   
    private String name;
    public SomeBody(String name)
    {
        this.name=name;
    }
   
    public void someBodyDo()
    {
        System.out.println(this.name+"早上7:45起床");
        //随机生成停顿时间,为了模拟较长的代码执行时间,以下都是类似
        try {
            int a=(int) ((long)1000* Math.random());
            System.out.println(a);
            Thread.sleep(a);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
       
        System.out.println(this.name+"7:50大便");
       
        try {
            int a=(int) ((long)1000* Math.random());
            System.out.println(a);
            Thread.sleep(a);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
       
        System.out.println(this.name+"8:00刷牙,顺带刷刷舌苔");
        try {
            int a=(int) ((long)1000* Math.random());
            System.out.println(a);
            Thread.sleep(a);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
       
        System.out.println(this.name+"8:05洗脸");
       
        try {
            int a=(int) ((long)1000* Math.random());
            System.out.println(a);
            Thread.sleep(a);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(this.name+"8:10搽雪花膏");
       
        try {
            int a=(int) ((long)1000* Math.random());
            System.out.println(a);
            Thread.sleep(a);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(this.name+"8:20到车站");
       
        try {
            int a=(int) ((long)1000* Math.random());
            System.out.println(a);
            Thread.sleep(a);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(this.name+"8:30搭上车");
        try {
            int a=(int) ((long)1000* Math.random());
            System.out.println(a);
            Thread.sleep(a);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
       
        System.out.println(this.name+"8:40到公司吃饭");
        try {
            int a=(int) ((long)1000* Math.random());
            System.out.println(a);
            Thread.sleep(a);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
       
        System.out.println(this.name+"8:50打开电脑发呆");
        try {
            int a=(int) ((long)1000* Math.random());
            System.out.println(a);
            Thread.sleep(a);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(this.name+"9:00开始工作");
    }

}

   

 

具有“打乱”代码执行顺序能力的Thead子类:SomeBodyWork类:

package wjj2;



public class SomeBodyWork extends Thread{


    private SomeBody person;


    public SomeBodyWork (SomeBody person)


    {


        this.person=person;


    }



    public void run()


    {


        person.someBodyDo();


    }



}


 

 

 

程序入口类:main类:

package wjj2;

public class main {
    public static void main(String args[])
    {
        SomeBody xiaoming=new SomeBody("小明 ");
        SomeBodyWork thread1=new SomeBodyWork(xiaoming);
        thread1.start();
        SomeBody  dagang=new SomeBody("大刚 ");
        SomeBodyWork thread2=new SomeBodyWork(dagang);
        thread2.start();
    }

}

 

程序执行结果如下:

 

 

小明 早上7:45起床
571
大刚 早上7:45起床
908
小明 7:50大便
395
大刚 7:50大便
583
小明 8:00刷牙,顺带刷刷舌苔
509
小明 8:05洗脸
25
大刚 8:00刷牙,顺带刷刷舌苔
224
小明 8:10搽雪花膏
962
大刚 8:05洗脸
292
大刚 8:10搽雪花膏
665
小明 8:20到车站
554
大刚 8:20到车站
278
大刚 8:30搭上车
934
小明 8:30搭上车
577
小明 8:40到公司吃饭
238
小明 8:50打开电脑发呆
812
大刚 8:40到公司吃饭
453
大刚 8:50打开电脑发呆
361
小明 9:00开始工作
大刚 9:00开始工作

 

 

通常Somebody自成一个单独的类,线程的目的就是:让 Somebody 并发地 doSomething

从程序的结果来看:

 1.两个线程的代码确实像图1一样,交替执行,且交替顺序是随机的,多试几次会发现执行结果会有不同。

 2.线程的run()方法提供了一种机制,一种把代码执行顺序“打乱”的机制,有些初学者喜欢在这个提供这个机制的类中,写一下“实体”类的方法,比如,他会让SomeBody类在SomeBodyWork中做一些事情,如

 

        System.out.println(this.name+"早上10:00开始编码");
        System.out.println(this.name+"早上11:00开始测试");
        System.out.println(this.name+"下午14:45开始修改代码");

 

 

 虽然Java中,什么东西都是对象,什么东西都以对象的模板“类”来展现,但类和类之间,还是有区别的,

很显然,以上3行代码,还是属于“实体”这些类的方法,应该移植在“实体”类中,Thread这个类,提供了一种机制,这种机制用“类”来展现,很神奇吧,呵呵。所以最好不要把杂七杂八的东东放在Thread的子类中,就是一个run()最好,Thread就是纯粹用来“并发”地run的。关于代码的归类,可以参考《重构--改善既有代码的设计》,Martin Fowler著,侯捷,熊节译,中国电力出版社。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章