黑马程序员——java基础---多线程

来源:互联网 发布:明星基金经理 知乎 编辑:程序博客网 时间:2024/05/09 04:11

黑马程序员——java基础—多线程

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


1、多线程的概念

  • 进程、线程、多线程的概念:

    • 进程:正在进行中的程序。
    • 线程:进程中一个负责程序执行的控制单元(执行路径)。
  • 多线程的好处:解决了多部分代码同时运行的问题。

  • 多线程的弊端:线程太多,会导致效率的降低。

2、创建线程方式一:继承Thread类

  1. 定义一个类继承Thread类。

  2. 覆盖Thread类中的run方法。

  3. 直接创建Thread的子类对象创建线程。

  4. 调用start方法开启线程并调用线程的run方法执行。

3、创建线程方式二:实现Runnable接口

  1. 定义类实现Runnable接口。

  2. 覆盖接口中的run方法,并将线程的任务代码封装到run方法中。

  3. 通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。

  4. 调用线程对象的start方法开启线程。

  5. 实现Runnable接口的好处:

    • 将线程的任务从线程的子类中分离出来,进行了单独的封装,按照面向对象的思想将任务封装成对象。
    • 避免了java单继承的局限。所以,创建线程的第二种方式较为常用。

4、线程的安全问题

  • 线程安全问题产生的原因:

    1. 多个线程在操作共享的数据。
    2. 操作共享数据的线程代码有多条。
  • 线程安全问题的解决方案

    • 思路:就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程不可以参与运算。必须要把这些代码都执行完毕后,其他线程才可以参与运算。

    • 在java中,用同步代码块就可以解决这个问题。(或者在函数上加synchronized修饰符即可)

    • 同步代码块的格式:

     synchronized(对象)    {        需要被同步的代码    }
  • 同步的特点:

    • 同步的好处:解决了线程的安全问题。

    • 同步的弊端:当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。

    • 同步的前提:必须是多个线程并使用同一把锁。

  • 利用同步代码块解决安全问题案例:

    • 需求:储户,两个,每个都到银行存钱,每次存100元,共存三次。
    • 思路:

      1. 定义一个Cons(储户)类并实现Runnable接口;

      2. 复习Cons类中的run方法,执行Bank(银行)类中存储这个动作;

      3. 将存储这个动作上锁,一次只能让有一个客户存钱。

      4. 创建两个线程并开启线程。

    • 代码:
class Bank{    private int sum;    public void add(int num)    {           synchronized(this)//保证每次只有一个线程在调用        {            sum+=num;            System.out.println("sum="+sum);        }    }}//实现Runnable接口class Cons implements Runnable{    Bank b=new Bank();    //复写Runnable中的run方法。    public void run()    {        for(int x=0;x<3;x++)        b.add(100);    }}class BankDemo {    public static void main(String[] args)     {        //把资源进行封装,传入给线程        Cons c=new Cons();        //创建两个线程        Thread t1=new Thread(c);        Thread t2=new Thread(c);        //开启线程        t1.start();        t2.start();    }}
  • 输出结果:

5、多线程下的单例模式

  • 饿汉式不存在安全问题,因为不存在多个线程共同操作数据的情况。

  • 懒汉式存在安全问题,可以使用同步函数解决。

    • 代码:
class Single{    private Single(){}    private static Single s=null;    public static Single getInstance()    {        //第一次判断,提高了效率,若对象已存在就不用判断锁了        if(s==null)        {            //保证每次只有一个线程在调用            synchronized(Single.class)            {                if(s==null)                    s=new Single();            }        }        return s;    }}

6、死锁

  • 定义:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限地阻塞,因此程序不可能正常终止。

  • 产生死锁的原因:

    1. 因为系统资源不足。
    2. 进程运行推进的顺序不合适。
    3. 资源分配不当等。
  • 产生死锁的四个必要条件:

    1. 互斥条件:一个资源每次只能被一个进程使用。
    2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
    3. 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
    4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
  • 示例代码:

class Test implements Runnable{    //flag是标识    private boolean flag;    Test(Boolean flag)    {        this.flag=flag;    }    public void run()    {        if(flag)        {            while (true)            {                //锁a                synchronized(MyLock.locka)                {                    System.out.println(Thread.currentThread().getName()+"……if lacka……");                }                //锁b                synchronized(MyLock.lockb)                {                    System.out.println(Thread.currentThread().getName()+"……if lackb……");                }            }                   }        else         {            while (true)            {                //锁b                synchronized(MyLock.lockb)                {                    System.out.println(Thread.currentThread().getName()+"……else lackb……");                }                //锁a                synchronized(MyLock.locka)                {                    System.out.println(Thread.currentThread().getName()+"……else lacka……");                }            }        }           }}class MyLock{    public static final Object locka=new Object();    public static final Object lockb=new Object();}class DeadLockDemo {    public static void main(String[] args)     {        Test t1=new Test(true);        Test t2=new Test(false);        //创建线程        Thread d1=new Thread(t1);        Thread d2=new Thread(t2);        //开启线程        d1.start();        d2.start();    }}

7、线程间通信

  • 等待/唤醒机制涉及的方法啊:
    1. wait():让线程处于冻结状态,被wait的线程会被存储到线程池中。
    2. notify():唤醒线程池中的一个线程(任何一个都有可能)
    3. notifyAll():唤醒线程池中的所有线程。
  • wait和sleep的区别:
    1. wait可以指定时间也可以不指定,sleep必须指定时间。
    2. 在同步中,对CPU的执行权和锁的处理不同。
      1. wait:释放执行权,释放锁。
      2. sleep:释放执行权,不释放锁。
  • 示例(生产者-消费者问题):
    • 代码:
class Resource{    private String name;    private String sex;    private boolean flag=false;    public synchronized void set(String name,String sex)    {        //flag为true时,线程t1冻结,等待唤醒        if(flag)            try            {                this.wait();                }            catch (InterruptedException e)            {                e.printStackTrace();            }        this.name=name;        this.sex=sex;        flag=true;        notify();    }    public synchronized void out()    {        //flag为false时,线程t2冻结,等待唤醒        if(!flag)            try            {                this.wait();            }            catch (InterruptedException e)            {                e.printStackTrace();            }            System.out.println(name+"……"+sex);            flag=false;            notify();    }}//输入,实现Runable接口class Input implements Runnable{    Resource r;    Input(Resource r)    {        this.r=r;    }    public void run()    {        int x=0;        while(true)        {            if (x==0)            {                r.set("小明","男");            }            else            {                r.set("小红","女");            }            x=(x+1)%2;        }    }}//输出,实现Runnable接口class Output implements Runnable{    Resource r;    Output(Resource r)    {        this.r=r;    }    public void run()    {        while(true)        {            r.out();        }    }}class ResourceDemo{    public static void main(String[] args)     {        //封装资源        Resource r=new Resource();        //创建任务        Input in=new Input(r);        Output out=new Output(r);        //创建线程        Thread t1=new Thread(in);        Thread t2=new Thread(out);        //开启线程        t1.start();        t2.start();    }}
  • 输出结果:
    这里写图片描述

8、JDK1.5新特性

  • JDK1.5以后将同步和锁封装成了对象,并将操作锁的隐式方式定义到了该对象中,将隐式动作变成了显式动作。
    • Lock接口:出现替代了同步代码块或者同步函数,将同步的隐式操作变成显示锁操作。同时更为灵活,可以一个锁上加上多组监视器。
    • lock():获取锁。
    • unlock():释放锁,为了防止异常出现,导致锁无法被关闭,所以锁的关闭动作要放在finally中。
    • Condition接口:出现替代了Object中的wait、notify、notifyAll方法。将这些监视器方法单独进行了封装,变成Condition监视器对象,可以任意锁进行组合。
      • Condition接口中的await方法对应于Object中的wait方法。
      • Condition接口中的signal方法对应于Object中的notify方法。
      • Condition接口中的signalAll方法对应于Object中的notifyAll方法。
  • 示例(生产者、多消费者问题:一个Lock、两个Condition)
    • 代码:
import java.util.concurrent.locks.*;class Resource{    private String name;    private int count=1;    private boolean flag=false;    //创建一个锁对象    Lock lock=new ReentrantLock();    //通过已有的锁获取该锁上的监视对象    Condition pro_con=lock.newCondition();    Condition con_con=lock.newCondition();    public void set(String name)    {        //获取锁        lock.lock ();        try        {            while(flag)            {                try                {                    //生产者监视器冻结                    pro_con.await();                }                catch (InterruptedException e)                {                    e.printStackTrace();                }            }                this.name=name+count;                count++;                System.out.println(Thread.currentThread().getName()+"……生产"+this.name);                flag=true;                //唤醒消费者监视器                con_con.signal();        }        finally        {            //释放锁            lock.unlock();        }    }    public void out()    {        //获取锁        lock.lock();        try        {            while(!flag)            {                try                {                    //消费者监视器冻结                    con_con.await();                }                catch (InterruptedException e)                {                    e.printStackTrace();                }            }            System.out.println(Thread.currentThread().getName()+"…… 消费"+name);            flag=false;            //唤醒生产者监视器            pro_con.signal();        }        finally        {            //释放锁            lock.unlock();        }    }}//输入class Producer implements Runnable{    Resource r;    Producer(Resource r)    {        this.r=r;    }    public void run()    {        while(true)        {            r.set("烤鸭");        }    }}//输出class Consumer implements Runnable{    Resource r;    Consumer(Resource r)    {        this.r=r;    }    public void run()    {        while(true)        {            r.out();        }    }}class ProducerConsumerDemo2{    public static void main(String[] args)     {        Resource r=new Resource();        Producer pro=new Producer(r);        Consumer con=new Consumer(r);        Thread t1=new Thread(pro);        Thread t2=new Thread(pro);        Thread t3=new Thread(con);        Thread t4=new Thread(con);        t1.start();        t2.start();        t3.start();        t4.start();    }}
  • 输出结果:
0 0
原创粉丝点击