黑马程序员_多线程

来源:互联网 发布:nginx 地址反向代理 编辑:程序博客网 时间:2024/05/14 05:48

----------- android培训java培训java学习型技术博客、期待与您交流! ------------ 

   
 

       进程就是一个正在执行中的程序;每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元;线程就是进程中一个独立的控制单元,控制着进程的执行;一个进程中至少有一个线程;而多线程就是 一个进程中有可能存在多条执行路径。
       如何在自定义的代码中,自定义一个线程呢?
       方法一: 
1、继承Thread
2、复写Thread类中的run方法
3、调用线程的start方法(启动线程,调用run方法)
        eg:
 class Demo extends Thread                              //继承Thread
{
        public void run()                                          //复写Thread类中的run方法
       {
                for(int x=0;x<60;x++)
                System.out.println("demo run----"+x);
        }
}

class ThreadDemo
{
        public static void main(String[] args)
        {
                 Demo d=new Demo();                         //创建好一个线程
                 d.start();                                             //启动线程,并调用run方法。
                 for(int x=0;x<60;x++)                           //主线程为了达到对比效果,也做循环输出语句
                 System.out.println("Hello World!--"+x);
         }
}

 PS:由于某一时刻执行哪个线程是cpu说了算,所以每次的打印结果都是不同的。这是多线程的一个特性:随机性。

 线程有四种基本状态:
1、被创建  
2、运行(start())
3、冻结(等待,睡眠。。)sleeptime)时间到了复活;wait()-notify()叫醒复活;
4、消亡(stop()或方法结束,注意:stop()方法已过时; ) 

特殊状态:临时状态(阻塞状态),具备运行资格,但没有执行权

获取线程对象以及名称:Thread.currentThread().getName(原来线程都有自己默认的对象,从Thread0开始)

 创建线程方法二:实现Runnable接口
 步骤:
1、定义类实现Runnable接口
2、覆盖Runnable接口中的run方法(将线程要运行的代码存放在该run方法中)
3、通过Thread类建立线程对象
4、将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数
5、调用Thread类的start方法开启线程,并调用Runnable接口子类的run方法

 两种方式的区别:
继承Thread:线程代码存放Thread子类run方法中
实现Runnable,线程代码存在在接口的子类的run方法中

实现Runnable接口的好处是:
        除接口外,java是不支持多继承的,而若用第一种方法,继承Thread类创建线程,则无法再继承其他类,为编程造成很多困扰和不便。

通过分析发现,多线程可能会出现安全问题

问题的原因:
        当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致了共享数据的错误。
解决办法:
       对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与运行。(即锁)

5.0版本更新后,JDK1.5中提供了多线程升级解决方案:
将同步Synchronized替换成显示的Lock操作。
Object中的waitnotifynotifyAll替换成了Condition对象。
升级版可以支持多个相关的Conditon对象

 多线程同步代码块的前提:
 1、必须要有两个或者两个以上的线程
 2、必须是多个线程使用同一个锁
好处:解决了多线程的安全问题
弊端:多个线程都需要判断所,较为消耗资源

当多线程出现问题时,如何寻找呢?

1、哪些是多线程运行代码

2、明确共享数据

3、明确多线程运行中哪些语句是操作共享数据的

同步有两种表现形式:同步函数和同步代码块其中同步函数要更简单


加入多线程的考虑,回顾单例式:

//饿汉式

class Single

{

    private static final Single s = new Single();  //只需加关键字final即可,只能创建一次。

    private Single(){}

   public static Single getInstance()

   {

       return s;

   }

}

//懒汉式

class Single

{

   private static Single s = null;

   private Single(){}

   public static Single getInstance()

   {

       if(s==null)                          //锁前先判断,一定程度提高了效率

       {

           synchronized(Single.class)       //以类名.class为唯一锁

           {

                if(s==null)                   //判断两次

               //--->A;

               s = new Single();

           }

       }

               return s;

   }

}

     由此可见,饿汉式更为简单方便,且单例模式为的就是调用其中的那个唯一的对象,所以,饿汉式应用的也更为普遍。


死锁的问题:
同步中嵌套同步,而锁却不同,很容易产生死锁现象。

线程间通讯,其实就是多个线程在操作同一个资源,但是操作的动作不同。为解决安全问题,出现了等待唤醒机制

conditon.await();               //等待

conditon.signalAll();            //唤醒所有

由于stop过时,要停止线程应如何操作呢?

定义循环结束标记

因为线程运行代码一般都是循环,只要控制了循环即可。

使用interrupt(中断)方法

该方法时结束线程的冻结状态,使线程回到运行状态中来。

Eg:

class StopThread implements Runnable

{

   private boolean flag =true;                //定义标记变量

   public  void run()                      //覆写run方法

   {

       while(flag)

       {

           System.out.println(Thread.currentThread().getName()+"....run");

       }

   }

   public void changeFlag()                  //如函数名一样,修改标记变量的值

   {

       flag = false;

   }

}

class  StopThreadDemo                      //终止线程的小例子

{

   public static void main(String[] args) 

   {

       StopThread st = new StopThread();

       Thread t1 = new Thread(st);            //创建了两个线程(都是Thread的子类)

       Thread t2 = new Thread(st);

       //t1.setDaemon(true);                  

       //t2.setDaemon(true);

       /*这里的setDaemon为设置守护线程,当所有线程都为守护线程时,JVM会自动退出。

         就像圣斗士星矢一样,圣斗士都是守护者守护着雅典娜,若没有了雅典娜,动画片中的

         五位圣斗士也就失去了存在的价值。若不注释掉两句设置守护线程的代码,则无需去

         run中判断,java虚拟机会直接退出。*/

       t1.start();                            //启动两个建立好的线程

       t2.start();

       int num = 0;                          //计数便于观察和理解

       while(true)                           

       //循环判断,若非如此,容易出现多个生产者和消费者不一一对应的情况。

       {

           if(num++ == 60)

           {

               //st.changeFlag();

               //t1.interrupt();                

               /*中断机制,将处于冻结状态的线程,强制恢复到运行状态中。非正常苏醒状态,比如

                 一个人被催眠后,催眠师出国了,毕老师狠劲一拳将其打醒,随然也达到了叫醒的

                 目的,但会受伤。即抛出异常:InterruptedException*/

               //t2.interrupt();

               break;

           }

           System.out.println(Thread.currentThread().getName()+"......."+num);

       }

       System.out.println("over");

   }

}
 

其它小知识点:

    Join方法A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。join可以用来临时加入线程执行。申请执行权)

    优先级:线程的默认优先级都是5,包括主线程,一共十级setPriority()用于更改线程的优先级,优先级越高,cpu执行该线程的频率越高,但不会不管其他线程。

yield()方法:暂停当前正在执行的线程对象,并执行其他线程。(临时停止)



 

----------- android培训java培训java学习型技术博客、期待与您交流! ------------ 

原创粉丝点击