zoukankan      html  css  js  c++  java
  • 黑马程序员----java基础:多线程

    ------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

    ------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

    ------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

      如果一次只完成一个事情,还是比较容易实现的,但事实上很多事情都是同时执行的,java为了模拟这种状态,引入了线程机制。

    当程序同时完成多个事情时,就是所谓的多线程程序。

      在一个时刻,单核的cpu只能运行一个程序。而我们看到的同时运行效果,只是cpu在多个进程间做着快速的随机切换动作。

      所以说多线程解决了多部分同时运行的问题,但是线程太多的话肯定会影响效率。

      首先老规矩一张图来展现我们本次要介绍的内容:

     一、实现方式

     实现线程的方式一共有两种---继承Thread类和实现Runnable接口。

    1、继承Thread类。

            a、定义类继承Thread。

            b、复写Thread中的run方法。

            c、创建定义类的实例对象。

            d、调用start方法启动线程。

     

    class TestThread extends Thread            // a、定义类继承Thread。
    {
        public void run()                    //b、复写Thread中的run方法。
        {
            for (int x = 0; x < 50 ;x++ )
            {
                System.out.println(Thread.currentThread().getName()+"......"+x);
            }
        }
    }
    
    class Demo
    {
        public static void main(String[] args) 
        {
            TestThread t1= new TestThread();//c、创建定义类的实例对象。
            TestThread t2= new TestThread();
            t1.start();                    //d、调用start方法启动线程。
            t2.start();
        }
    }
       

    2、实现runnable接口

       a、定义类实现Runnable的接口。

            b、覆盖Runnable接口中的run方法。

            c、通过Thread类创建线程对象。

            d、调用start方法启动线程。

    class TestThread implements Runnable            //a、定义类实现Runnable的接口。
    {
    	public void run()				// b、覆盖Runnable接口中的run方法。
    	{
    		for (int x = 0; x < 50 ;x++ )
    		{
    			System.out.println(Thread.currentThread()+"++++++"+x);
    		}
    	}
    }
    class Demo
    {
    	public static void main(String[] args) 
    	{
    
    		TestThread d = new TestThread();	
    		Thread t1 = new Thread(d);
    		Thread t2 = new Thread(d);		//c、通过Thread类创建线程对象。
    		t1.start();			     //d、调用start方法启动线程。
    		t2.start();
    	}
    }
    

    3、实现Runnable接口的优势

      实际开发中,我们经常使用第二种方式做多线程,它的优势在于

      a、避免了按照面向对象的思想将任务封装成对象。

      b、避免了java单继承的局限性。

     

     

     

     二、线程的生命周期

    线程具有生命周期,其各个生命周期的关系如下图。

     

     

     

     

     三、线程安全

      实际开发中,使用多线程的情况有很多,比如火车站售票系统,当有多个线程做取票动作时,假设此时只有一张票,一线程将票售出,此时二线程完成了判断是否有票动作,并得出票数大于0结论。此时执行售票就会产生负数。

    1、安全问题产生的原因

      a、多个线程在操纵共享数据

      b、操纵共享数据的代码有多条。

    2、解决办法--同步

      将多条操作共享数据的线程代码封装起来,当有线程执行这些代码时候,其他线程不可参与。

      a、同步代码块

      

    synchronized(obj)
    {
      //需要同步的代码 }

      b、同步函数

    public synchronized void show()  
    {  
            //需要同步的代码
    }     

    3、同步函数和同步代码块的区别

      同步函数的锁是this

      同步代码块的锁是任意的(也可以是this)

      实际开发中建议用同步代码块。

    4、同步的利弊

            好处:解决了多线程的安全问题。

            弊端:多个线程需要判断锁,较为消耗资源。

     5、静态函数代码块

      如果同步函数被静态修饰后,使用的锁是什么呢?设计模式中的单例模式给大家展示。

    //懒汉式
    class Single
    {
        private static Single s = null;
    
        private Single(){}
    
        public static Single getInstance()
        {
            if(s == null)
            {
                synchronized(Single.class)    //静态方法写---类名.class
                {
                    if(s==null)
                        s = new Single();
                }
            }
            return s;
        }
    }

     

     四、线程间通信

     线程间的通信其实就是多个线程共享同一个资源。

    class  ResorceDemo
    {
        public static void main(String[] args) 
        {
            Resorce r = new Resorce();
            Input in = new Input(r);
            Output out = new Output(r);
            Thread t1 = new Thread(in);
            Thread t2 = new Thread(out);
            t1.start();
            t2.start();
        }
    }
    class Resorce //定义一个资源
    {
        String name;
        String sex;
        boolean flag = false;
    }
    
    class Input implements Runnable
    //写入资源
    {
        Resorce r;
        Input(Resorce r)
        {
            this.r = r;
        }
        public void run()//复写run方法
        {
            int x = 0;//x相当于一个标志.
            while (true)
            {
                synchronized(r)
                {
                    if (r.flag == true)
                    {
                        try
                        {
                            r.wait(); 
                        }
                        catch (InterruptedException e)
                        {
                        }
                        
                    }
                    else
                    {
                        if (x==0)
                        {
                            r.name = "mike";
                            r.sex = "nan";
                        }
                        else
                        {
                            r.name = "傻妞";
                            r.sex = "妞妞妞妞i牛i牛ii牛";
                        }
                        r.flag = true;
                        r.notify();//唤醒
                    }
                        x = (x+1)%2;//x在0和1之间相互交替.
                    
                    
                    
                }    
            }
        }
    }
    class Output implements Runnable
    //读取资源
    {
        Resorce r;
        Output(Resorce r)
        {
            this.r = r;
        }
        public void run()
        {
            while (true)//无限循环
            {
                synchronized(r)
                {
                    if (r.flag == false)
                    {
                        try
                        {
                            r.wait(); 
                        }
                        catch (InterruptedException e)
                        {
                        }
                        
                    }
                    else
                    {
                        System.out.println(r.name + "..."+r.sex);
                        r.flag = false;
                        r.notify();
                    }
                    
                }
                
            }
        }
    }

     

     

     五、常见操作方法

    1,wait(): 让线程处于冻结状态,被wait的线程会被存储到线程池中。
    2,notify():唤醒线程池中一个线程(任意).
    3,notifyAll():唤醒线程池中的所有线程。
    4,setPriority():设置优先级
    5,yield():暂停当前线程,让其他线程执行。

     

    当然,这些常见的操作方法在API中都可以查到,这不是我要说的重点,下面来对这些方法做一下比较。

     

    (1)wait 和 sleep 区别?

      1,wait可以指定时间也可以不指定。    sleep必须指定时间。

      2,在同步中时,对cpu的执行权和锁的处理不同。  wait:释放执行权,释放锁。  sleep:释放执行权,不释放锁。

    (2)wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中?

                    a,这些方法存在与同步中。

                    b,使用这些方法时必须要标识所属的同步的锁。同一个锁上wait的线程,只可以被同一个锁上的notify唤醒。

                    c,锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。

     六、总结

     多线程本身也是比较复杂的问题,要完全理解它还需要时间,所谓孰能生巧,这些知识还是要多看多练多去实践才能真正掌握。

     

     

     

  • 相关阅读:
    sql server 查询出的结果集,拼接某一列赋值给一个变量
    sql server显示某一列中有重复值的行
    webservice 尝试加载 Oracle 客户端库时引发 BadImageFormatException。如果在安装 32 位 Oracle 客户端组件的情况下运行,将出现此问题
    Merge Into 用法
    修改TFS与本地源代码映射路径
    Thinkphp5.0第五篇
    aircrack-ng wifi密码破解
    Thinkphp5.0第四篇
    Thinkphp5.0第三篇
    Thinkphp5.0第二篇
  • 原文地址:https://www.cnblogs.com/jinfulin/p/4109076.html
Copyright © 2011-2022 走看看