黑马程序员——多线程

——- android培训java培训、期待与您交流! ———-



黑马程序员——多线程

创建线程方式一

  • 继承Thread类
    1. 子类覆盖父类中的run方法,将线程运行的代码存放在run中。
    2. 建立子类对象的同时线程也被创建。
    3. 通过调用start方法开启线程。
/*
 创建线程方式一:继承Thread类。

 步骤:
 1,定义一个类继承Thread类。
 2,覆盖Thread类中的run方法。
 3,直接创建Thread的子类对象创建线程。
 4,调用start方法开启线程并调用线程的任务run方法执行。

 可以通过Thread的getName获取线程的名称 Thread-编号(从0开始)

 主线程的名字就是main。
 */

class Demo extends Thread {
    private String name;

    Demo(String name) {
        super(name);
        // this.name = name;
    }

    public void run() {
        for (int x = 0; x < 10; x++) {
            // for(int y=-9999999; y<999999999; y++){}
            System.out.println(name + "....x=" + x + ".....name="
                    + Thread.currentThread().getName());
        }
    }
}

class ThreadDemo1 {
    public static void main(String[] args) {

        /*
         * 创建线程的目的是为了开启一条执行路径,去运行指定的代码和其他代码实现同时运行。
         * 
         * 而运行的指定代码就是这个执行路径的任务。
         * 
         * jvm创建的主线程的任务都定义在了主函数中。
         * 
         * 而自定义的线程它的任务在哪儿呢? Thread类用于描述线程,线程是需要任务的。所以Thread类也对任务的描述。
         * 这个任务就通过Thread类中的run方法来体现。也就是说,run方法就是封装自定义线程运行任务的函数。
         * 
         * run方法中定义就是线程要运行的任务代码。
         * 
         * 开启线程是为了运行指定代码,所以只有继承Thread类,并复写run方法。 将运行的代码定义在run方法中即可。
         */
        // Thread t1 = new Thread();

        Demo d1 = new Demo("旺财");
        Demo d2 = new Demo("xiaoqiang");
        d1.start();// 开启线程,调用run方法。

        d2.start();
        System.out.println("over...." + Thread.currentThread().getName());
    }
}

线程的四种状态

线程的四种状态

sleep方法需要指定睡眠时间,单位是毫秒。
一个特殊的状态:就绪。具备了执行资格,但是还没有获取资源。

创建线程方式二

  • 实现Runnable接口
    1. 子类覆盖接口中的run方法。
    2. 通过Thread类创建线程,并将实现了Runnable接口的子类对象作为参数传递给Thread类的构造函数。
    3. Thread类对象调用start方法开启线程。
/*
创建线程的第一种方式:继承Thread类。

创建线程的第二种方式:实现Runnable接口。

1,定义类实现Runnable接口。
2,覆盖接口中的run方法,将线程的任务代码封装到run方法中。
3,通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。
    为什么?因为线程的任务都封装在Runnable接口子类对象的run方法中。
    所以要在线程对象创建时就必须明确要运行的任务。

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

实现Runnable接口的好处:
1,将线程的任务从线程的子类中分离出来,进行了单独的封装。
    按照面向对象的思想将任务的封装成对象。
2,避免了java单继承的局限性。

所以,创建线程的第二种方式较为常用。
 */

class Demo implements Runnable// 准备扩展Demo类的功能,让其中的内容可以作为线程的任务执行。// 通过接口的形式完成。
{
    public void run() {
        show();
    }

    public void show() {
        for (int x = 0; x < 20; x++) {
            System.out.println(Thread.currentThread().getName() + "....." + x);
        }
    }
}

class ThreadDemo2 {
    public static void main(String[] args) {
        Demo d = new Demo();
        Thread t1 = new Thread(d);
        Thread t2 = new Thread(d);
        t1.start();
        t2.start();

        // Demo d1 = new Demo();
        // Demo d2 = new Demo();
        // d1.start();
        // d2.start();
    }
}

线程安全问题

  • 导致安全问题的出现的原因:
    • 多个线程访问出现延迟。
    • 线程随机性 。
  • 注:线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大的。

同步(synchronized)

  • 格式:

    synchronized(对象)
    {
        需要同步的代码;
    }

同步可以解决安全问题的根本原因就在那个对象上。
该对象如同锁的功能。

  • 同步的特点
    • 同步的前提:
      • 同步需要两个或者两个以上的线程。
      • 多个线程使用的是同一个锁。
    • 未满足这两个条件,不能称其为同步。
    • 同步的弊端:
      • 当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
  • 同步函数
    • 格式:
      在函数上加上synchronized修饰符即可。

线程间通信

线程间通信

  • 思考1:wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中?
    1. 这些方法存在与同步中。
    2. 使用这些方法时必须要标识所属的同步的锁。
    3. 锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。
  • 思考2:wait(),sleep()有什么区别?
    • wait():释放cpu执行权,释放锁。
    • sleep():释放cpu执行权,不释放锁。

停止线程

  1. 定义循环结束标记
    • 因为线程运行代码一般都是循环,只要控制了循环即可。
  2. 使用interrupt(中断)方法。
    • 该方法是结束线程的冻结状态,使线程回到运行状态中来。

注:stop方法已经过时不再使用。

经典案例:生产者消费者

/*
 生产者,消费者。

 多生产者,多消费者的问题。
 if判断标记,只有一次,会导致不该运行的线程运行了。出现了数据错误的情况。
 while判断标记,解决了线程获取执行权后,是否要运行!

 notify:只能唤醒一个线程,如果本方唤醒了本方,没有意义。而且while判断标记+notify会导致死锁。
 notifyAll解决了本方线程一定会唤醒对方线程的问题。
 */

class Resource {
    private String name;
    private int count = 1;
    private boolean flag = false;

    public synchronized void set(String name)//
    {
        while (flag)
            try {
                this.wait();
            } catch (InterruptedException e) {
            }// t1 t0

        this.name = name + count;// 烤鸭1 烤鸭2 烤鸭3
        count++;// 2 3 4
        System.out.println(Thread.currentThread().getName() + "...生产者..."
                + this.name);// 生产烤鸭1 生产烤鸭2 生产烤鸭3
        flag = true;
        notifyAll();
    }

    public synchronized void out()// t3
    {
        while (!flag)
            try {
                this.wait();
            } catch (InterruptedException e) {
            } // t2 t3
        System.out.println(Thread.currentThread().getName() + "...消费者........"
                + this.name);// 消费烤鸭1
        flag = false;
        notifyAll();
    }
}

class Producer implements Runnable {
    private Resource r;

    Producer(Resource r) {
        this.r = r;
    }

    public void run() {
        while (true) {
            r.set("烤鸭");
        }
    }
}

class Consumer implements Runnable {
    private Resource r;

    Consumer(Resource r) {
        this.r = r;
    }

    public void run() {
        while (true) {
            r.out();
        }
    }
}

class ProducerConsumerDemo {
    public static void main(String[] args) {
        Resource r = new Resource();
        Producer pro = new Producer(r);
        Consumer con = new Consumer(r);

        Thread t0 = new Thread(pro);
        Thread t1 = new Thread(pro);
        Thread t2 = new Thread(con);
        Thread t3 = new Thread(con);
        t0.start();
        t1.start();
        t2.start();
        t3.start();

    }
}

使用Lock接口:

/*
jdk1.5以后将同步和锁封装成了对象。 
并将操作锁的隐式方式定义到了该对象中,
将隐式动作变成了显示动作。

Lock接口: 出现替代了同步代码块或者同步函数。将同步的隐式锁操作变成显示锁操作。
同时更为灵活。可以一个锁上加上多组监视器。
lock():获取锁。
unlock():释放锁,通常需要定义finally代码块中。

Condition接口:出现替代了Object中的wait notify notifyAll方法。
            将这些监视器方法单独进行了封装,变成Condition监视器对象。
            可以任意锁进行组合。
await();
signal();
signalAll();
 */

import java.util.concurrent.locks.*;

class Resource {
    private String name;
    private int count = 1;
    private boolean flag = false;

    // 创建一个锁对象。
    Lock lock = new ReentrantLock();

    // 通过已有的锁获取该锁上的监视器对象。
    // Condition con = lock.newCondition();

    // 通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者。
    Condition producer_con = lock.newCondition();
    Condition consumer_con = lock.newCondition();

    public void set(String name)// t0 t1
    {
        lock.lock();
        try {
            while (flag)
                // try{lock.wait();}catch(InterruptedException e){}// t1 t0
                try {
                    producer_con.await();
                } catch (InterruptedException e) {
                }// t1 t0

            this.name = name + count;// 烤鸭1 烤鸭2 烤鸭3
            count++;// 2 3 4
            System.out.println(Thread.currentThread().getName()
                    + "...生产者5.0..." + this.name);// 生产烤鸭1 生产烤鸭2 生产烤鸭3
            flag = true;
            // notifyAll();
            // con.signalAll();
            consumer_con.signal();
        } finally {
            lock.unlock();
        }

    }

    public void out()// t2 t3
    {
        lock.lock();
        try {
            while (!flag)
                // try{this.wait();}catch(InterruptedException e){} //t2 t3
                try {
                    cousumer_con.await();
                } catch (InterruptedException e) {
                } // t2 t3
            System.out.println(Thread.currentThread().getName()
                    + "...消费者.5.0......." + this.name);// 消费烤鸭1
            flag = false;
            // notifyAll();
            // con.signalAll();
            producer_con.signal();
        } finally {
            lock.unlock();
        }

    }
}

class Producer implements Runnable {
    private Resource r;

    Producer(Resource r) {
        this.r = r;
    }

    public void run() {
        while (true) {
            r.set("烤鸭");
        }
    }
}

class Consumer implements Runnable {
    private 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 t0 = new Thread(pro);
        Thread t1 = new Thread(pro);
        Thread t2 = new Thread(con);
        Thread t3 = new Thread(con);
        t0.start();
        t1.start();
        t2.start();
        t3.start();

    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值