A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© HM王琦 中级黑马   /  2013-3-1 10:54  /  1406 人查看  /  1 人回复  /   1 人收藏 转载请遵从CC协议 禁止商业使用本文


/*
        java虚拟机允许程序并发地运行多个线程。
        JVM启动至少有两个线程,一个是主线程(代码在mian方法中);另一个是负责垃圾回收机制的线程。
       
        创建线程
        方法一
           在java.lang包中有一个Thread类
        继承Thread类
        步骤:
        1,定义类继承Thread.
        2,复写Threa类中的run方法;
                目的:将自定义代码存储在run方法中,让线程运行。
        3,调用start(),该方法有两个作用,就是启动线程,调用run方法。

*/
//        例:三个窗口分别买票,每一个窗口卖10张票

        class ThreadDemo extends Thread        //定义类继承Thread.
        {
               
                public void run()        //复习run方法
                {       
                        int x=1;
                        while(x<=10)
                        {
                                System.out.println(Thread.currentThread().getName()+"....."+x);//打印正在运行的线程
                                x++;
                        }
                }
        }
        class Test
        {
                public static void main(String[] String)
                {
                        new ThreadDemo().start();//创建第一个线程并运行
                        new ThreadDemo().start();//创建第二个线程并运行
                        new ThreadDemo().start();//创建第三个线程并运行
                }
        }
       
        //要点:run方法用于存放线程要运行的代码。
/*
        方法二:通过实现Runable接口
        步骤:
        1,定义类实现(implements)Runnable接口;
        2,覆盖Runnable接口的run()方法
        3,通过Thread类建立线程对象。
        4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
        5,调用Thread类的start方法开启线程并调用Runnable的run方法。

        实现方式与继承方式的区别
        继承Thread:线程代码存放在Thread子类的run方法中。
        实现Runnable:线程代码存放在Runnable接口子类的run方法中。
        */

        //例如:购票小程序
        //有100张票三个窗口卖。三个窗口共享100张票。
        class ThreadDemo2 implements Runnable//定义类实现(implements)Runnable接口;
        {
                private int tick=10;
                public void run()//覆盖Runnable接口的run()方法
                {
                        while(true)
                        {
                                if(tick>0)
                                {
                                        //为了问题看起来明显一点在此睡上10毫秒,10好秒后自动回到可执行状态
                                        try{Thread.sleep(100);} catch(Exception e){}
                                        System.out.println(Thread.currentThread().getName()+"......."+tick--);
                                }
                               
                        }
                }
        }
        class Test2
        {
                public static void main(String[] args)
                {
                        ThreadDemo2 th=new ThreadDemo2();//创建ThreadDemo2对象,
                        new Thread(th).start();//将th作为参数传递给Thread创建线程对象
                        new Thread(th).start();//同上
                        new Thread(th).start();//同上
                }
        }
        //以上程序是不安全的,存在线程安全性问题。此多线程操做的是共享数据,因为一个线程对多条语句只执行了一部分,还没完,这时另一个线程进来执行
        //导致共享数据错误。
        /*
        线程的周期
        一个线程有4种状态,任何一个线程肯定处于这4中状态中的一种。
        1,初始状态(New):当线程对象被建立之后,并在调用start()方法之前。此时线程处于初始状态。
           这时线程对象已经产生,但是没被启动。此时可调用start()启动线程和stop()方法停止线程。
        2,可执行状态(Runnable):当线程对象调用start方法后,线程就转入可执行状态。当有多个线程处于可执行状态时,
           这些线程都一个线程池中,排程器会按照先后顺序排列它们,当有CPU资源时,排程器会依次启动这些线程。
           所以,处于可执行状态的线程可能在它正在执行,也可能在线程池中等待。
        3,阻塞状态(NonRunnable):如线程对象调用wait()方法后,线程就处于阻塞状态。
           此时排程器会忽略它,不对它进行排程。由阻塞状态可以通过resume()方法恢复多可执行状态,它有可能重新执行。
        4,退出状态(Done):当一个线程正常结束或调用stop()方法。这个线程就退出了。

        控制线程周期的方法
        strat()  开启一个线程,由New状态Runnable状态
        stop()         结束一个线程,由初始状态或可执行状态转入Done状态。
        sleep(long) 线程暂停一段时间。由Runnable状态转入NonRunnable状态,时间到自动返回可执行状态。
        wait() 停止正在运行的线程,由Runnable状态转入NonRunnable状态。需要notify()唤醒。
        notify();唤醒由wait()停止的线程。
        notifyAll() 唤醒所有等待的线程。
        suspend() 挂起执行,由Runnable状态转入NonRunnable状态。
        resume() 恢复执行,由NonRunnable状态转入Runnable状态。
        yield() 明确放弃执行。线程由Runnable状态转入Runnable状态。

       
        多线程的安全性问题
        当多线程在操作共享数据是,就会出现安全性问题。因为一个线程对多条语句只执行了一部分,还没完,这时另一个线程进来执行
        导致共享数据错误。

        解决办法就是:同步代码块。
        同步代码块原理:当多线程操作共享数据时,一个线程执行完,才让另一个线程进来执行。
        格式:
        synchronized(对象)  此对象可以是任意的
        {
                需要被执行的代码(被多线程操作的共享数据)
        }
       
        同步代码块中对象如同锁,就是当一个线程执行到操作同步代码时,就把它锁住,不让其他的线程进入执行。
        当此线程执行操作完时,就释放锁。这时其他的线程才可以继续执行同步代码。
       
        同步的前提:
        1,必须有两个以上的线程
        3,多线程必须使用同一个锁
       
        同步的好处:解决了多线程的安全性问题
        同步的弊端:较为消耗资源

        同步函数
        同步函数只需要在方法前加synchronized修饰即可
        同步非静态方法的锁是 this
        而同步静态方法的锁是 类名.Class() 因为静态进入内有存时,还没本类对象,只有该类对应的字节码文件对象。
*/
//解决上述程序的线程安全性问题。
//代码如下:
class ThreadDemo3 implements Runnable
{
        private int tick=10;
        //复写Runnable接口的run方法
        public void run()
        {
                while(true)
                {       
                        //让线程暂停一会儿后自动回去可执行状态
                        try{Thread.sleep(100);}catch(Exception e){}
                        synchronized(new Object())
                        {
                                if(tick>0)
                                {
                                        System.out.println(Thread.currentThread().getName()+"...."+tick--);
                                }
                        }
               
                }
        }
}
class Test3
{
        public static void main(String[] args)
        {
                ThreadDemo3 th=new ThreadDemo3();
                new Thread(th).start();
                new Thread(th).start();
                new Thread(th).start();
        }
}

评分

参与人数 1黑马币 +6 收起 理由
陈丽莉 + 6 支持分享~

查看全部评分

1 个回复

倒序浏览
值得学习ing!
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马