java中的多线程

来源:互联网 发布:mac美国官网怎么下单 编辑:程序博客网 时间:2024/06/08 11:59
今天在博客园学习了一篇博客《java中的多线程你只需要看一篇就够了》下面给出这篇博客的链接,想学习的朋友也可以去看看,写的十分详细。

链接:java中的多线程你只需要看一篇就够了

下面是自己对于多线程的理解,前一篇文章也说过程序,进程,线程的关系。如果有不懂的可以去查阅上一篇博客《程序和进程的区别》。

并发:
通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。打个比例,用户可以使用计算机听歌,也可以来打印文件,这些活动是完全可以同时进行的。那么听歌,打印文件就是一个个线程。

并行:
多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。

单线程:
一个进程只拥有一个线程,程序代码按调用顺序依次往下执行。

多线程:
在程序中可以执行多个线程,每个线程完成一个功能,并与其它线程并发执行,这种机制就叫多线程。用多线程只有一个目的,那就是更好的利用cpu的资源。
注意:Java中的多线程是一种抢占机制而不是分时机制。抢占机制指的是有多个线程处于可运行状态,但是只允许一个线程在运行,他们通过竞争的方式抢占CPU。

实现线程的俩种方式:继承Thread类和实现Runnable接口

/* * 使用继承java.lang.Thread类的方式建立线程 */public class ThreadTestDemo extends Thread{    /*     * 完成线程的真正功能放在run()方法中,继承Thread类,就需要重写(Override)run()方法     * 然后同时调用Thread类的start()方法执行线程,JVM会自动调用这个方法     * (non-Javadoc)     * @see java.lang.Thread#run()     */    public void run()    {        System.out.println("I’m running!(Override)");    }    /*     * 注意重载(Overload)run(),该run()方法和普通方法一样,就算调用了Thread类的start()方法,JVM也不会调用这个方法     */    public void run(int times)    {        System.out.println("I'm running!(Overload)");    }    public static void main(String[] args)    {        new ThreadTestDemo().start();    }}

测试结果:
这里写图片描述

注意:
不建议用此方法定义线程,因为采用继承Thread类的方法定义线程后,你就不能再继承其他类了,导致了程序的扩展性大大降低!

/* * 通过实现Runnable接口创建一个线程 *  */public class ThreadRunnableTestDemo implements Runnable{    /*     * 实现接口必须实现其run()方法     * (non-Javadoc)     * @see java.lang.Runnable#run()     */    public void run()    {        System.out.println("I'm running!");    }    /*     *启动一个新的线程,不是直接调用Thread子类对象的run方法      * 是调用Thread子类的start()方法产生一个新的线程,该线程运行Thread子类的run()方法     */    public static void main(String[] args)    {        ThreadRunnableTestDemo a=new ThreadRunnableTestDemo();        Thread t=new Thread(a);        t.start();    }}

调试结果:
这里写图片描述

总结:

实现Runnable接口比继承Thread类所具有的优势:

1):适合多个相同的程序代码的线程去处理同一个资源

2):可以避免java中的单继承的限制

3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立

线程生命周期的各种状态
这里写图片描述
虽然多线程看起来像同时执行,但事实上在同一时间点上只有一个线程执行,只是线程之间切换较快,让人产生线程是同时进行的假象。

线程的方法和属性:
1)优先级(priority)
并没有优先级高的进程先执行这一说话,只是优先级高的线程获得执行的概率高于优先级低的线程。可以用setPriority(“ThreadName”,优先级1-9,Thread对象)来设置线程优先级高低。

2)线程休眠(Thread.sleep(long millis))
参数以毫秒为单位,可以使得线程在times/1000秒内不会进入就绪状态,醒来以后也不能保证其进入运行状态,只能保证其进入就绪状态。
作用:保持对象锁,让出CPU,调用目的是不让当前线程独自霸占该进程所获取的CPU资源,以留一定的时间给其他线程执行的机会;

3)线程加入(Thread.join())
当某个线程使用join()方法加入到另外一个线程时,另一个线程会等待该线程执行完毕后再继续执行。

4)线程礼让(Thread.yield())
让出CPU的使用权,给其他线程执行机会、让同等优先权的线程运行。(但并不保证当前线程会被JVM再次调度、使该线程重新进入Running状态),如果没有同等优先权的线程,那么yield()方法将不会起作用。

5)线程等待(Obejct.wait())
当一个线程执行到wait()方法时,他就进入到一个和该对象相关的等待池(Waiting Pool)中,同时失去了对象的机锁—暂时的,wait后还要返还对象锁。当前线程必须拥有当前对象的锁,如果当前线程不是此锁的拥有者,会抛出IllegalMonitorStateException异常,所以wait()必须在synchronized block中调用。

6)线程中断(Thread.interrupt())
现在已经不建议使用stop()方法来停止一个线程的运行,提倡在run()方法中使用无限循环的形式,然后使用一个布尔型标记控制循环的停止。

/* * 操作线程的方法 */public class ThreadMethodDemo implements Runnable{    /*     * 线程停止     */    private boolean isContinue=false;    //设置一个Boolean类型标记变量,默认false    public void run()    {        while(true)        {            //...            if(isContinue)         //当isContinue为true时,停止线程            {                break;            }        }    }    /**定义设置isContinue变量为true的方法**/    public void setContinue()    {        this.isContinue=true;       }}

如果线程是因为使用了sleep()或者wait()方法进入了就绪状态,可以使用interrupt()方法使线程离开run()方法,同时结束线程,程序会抛出InterruptedException异常,用户可以在处理异常时完成线程的中断业务处理,如终止while循环。

7)线程的唤醒(Object.notify()/notifyAll())
唤醒在当前对象等待池中等待的第一个线程/所有线程。notify()/notifyAll()也必须拥有相同对象锁,否则也会抛出IllegalMonitorStateException异常。因此如果运行的线程调用了wait()方法,必须使用notify()方法唤醒。

8)线程同步机制(Synchronizing Block)
Synchronized Block/方法控制对类成员变量的访问来解决资源共享问题,就好比一个人上厕所,他进入洗手间需要锁门,门就是锁,出来再打开锁让其他人使用。这样就可以有效地防止资源冲突。关键字synchronized。

以卖火车票为例,如果不使用同步机制,则会产生下面出现的情况。

/* * 模拟火车站售票系统 * 不使用synchronized同步机制,会导致车票资源被多个线程同时共享,导致车票票数出现负数情况 */public class ThreadSafeTestDemo implements Runnable{    int num=10;     public void run()    {        while(true)        {            if(num>0)            {                try {                    Thread.sleep(100);                } catch (InterruptedException e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }                System.out.println("tickes:"+num--);            }        }    }    public static void main(String[] args)    {        /**模拟四个窗口卖票**/        ThreadSafeTestDemo t=new ThreadSafeTestDemo();        Thread tA=new Thread(t);        Thread tB=new Thread(t);        Thread tC=new Thread(t);        Thread tD=new Thread(t);        /*         * 当线程A执行run()方法时,还没来得及进行递减操作,就执行其调用sleep()方法进入就绪状态         * 此时线程B,C,D都进入了run()方法,发现num>0依然成立         * 但此时线程1休眠时间到,将num变量递减,同时线程B,C,D也对num变量进行递减操作,从而产生负值         */        tA.start();        tB.start();        tC.start();        tD.start();    }}

调试结果:
这里写图片描述

使用线程同步机制就不会出现上面出现票数为负的情况。

/* * 使用synchronized同步机制模拟火车站卖票系统 */public class ThreadSynchronizedTestDemo implements Runnable{    int num=10;    /*     * 定义一个同步方法     * 当某个对象调用了同步方法时,该对象上的其他同步方法必须等待该同步方法执行完毕后才能被执行     * 必须将每一个能访问共享资源的方法修饰为synchronized     */    public synchronized void doit()    {        while(true)        {            if(num>0)            {                try {                    Thread.sleep(100);                } catch (InterruptedException e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }                /*如果是num--则票无法卖完,num的值无法为0,解决方法可以使条件为num>=0*/                System.out.println("tickes:"+--num);            }        }    }    public void run()    {        while(true)        {            doit();        }    }    public static void main(String[] args)    {        ThreadSynchronizedTestDemo t=new ThreadSynchronizedTestDemo();        Thread tA=new Thread(t);        Thread tB=new Thread(t);        Thread tC=new Thread(t);        Thread tD=new Thread(t);        tA.start();        tB.start();        tC.start();        tD.start();    }}

调试结果:
这里写图片描述

总结:
线程处于就绪状态的几种方法:
1.调用sleep()方法。
2.调用wait()方法。
3.等待输入/输出完成。

线程处于就绪状态后,使得线程再次进入运行状态的几种方法:
1.调用notify()/notifyAll()方法。
2.调用interrupt()方法。
3.线程的休眠时间结束。
4.输入/输出结束。

1 0
原创粉丝点击