Java多线程

目录

线程创建方式

1、继承Thread类

2、实现Runnale接口

 为什么有实现Runnable接口这种创建线程的方式?

3、实现Callable接口

线程优先级

Thread.sleep()

Thread.join()

线程同步与安全 synchronized

线程同步与安全 Lock

多线程生产者消费者wait() notify() notifyAll()


线程创建方式

1、继承Thread类

        子类继承Thread类

        重写run方法

        new 子类即创建线程

        调用start方法即启动线程

class Test {
    public static void main(String[] args) {
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();
        thread1.setName("Thread1");
        thread2.setName("Thread2");
        thread1.start();
        thread2.start();
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(getName() +":" + i);
        }
    }
}

2、实现Runnale接口

        子类实现Runnable接口

        重写run方法

        new子类实例作为new Thread的target创建线程

        调用start启动线程

class Test {
    public static void main(String[] args) {
        Thread thread1 = new Thread(new MyThread("Thread1"));
        Thread thread2 = new Thread(new MyThread("Thread2"));
        thread1.start();
        thread2.start();
    }
}

class MyThread implements Runnable {
    private String name; // 线程名字

    public MyThread(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(name + ":" + i);
        }
    }
}

 为什么有实现Runnable接口这种创建线程的方式?

        避免子类继承Thread类后,无法继承其他类的单继承缺陷

        子类实现Runnable接口,用子类的实例作为new Thread的target,这样很好实现了线程和程序的分离很好实现多个程序处理同一个资源

3、实现Callable接口

        子类实现Callable接口,重写他的call方法,该方法有返回值

        new FutureTask实例来包装new子类实例

        FutureTask实例作为new Thread的target创建线程

        start启动

        FutureTask的实例调用get方法可以得到call的返回值,get方法会抛异常

class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyThread a = new MyThread("A");
        MyThread b = new MyThread("B");
        FutureTask<MyThread> futureTask1 = new FutureTask<MyThread>(a);
        FutureTask<MyThread> futureTask2 = new FutureTask<MyThread>(b);

        Thread thread1 = new Thread(futureTask1);
        Thread thread2 = new Thread(futureTask2);
        thread1.start();
        thread2.start();

        System.out.println(futureTask1.get());
        System.out.println(futureTask2.get());
    }
}

class MyThread implements Callable {
    private String name;

    public MyThread(String name) {
        this.name = name;
    }

    @Override
    public Object call() throws Exception {
        for (int i = 1; i <= 100; i++) {
            System.out.println(name + ":" + i);
        }
        return name;
    }
}

可以看到两个线程在交错执行,没有出现等待谁执行完成后再执行的情况。这就是多线程最直观的案例

线程优先级

高优先级的线程与低优先级的比,会被优先考虑执行。而不是一定,这就是抢占式调度的概念。在运行次数非常多的时候这种优先是很明显的。

程序运行过程中,一些线程创建了新的线程对象,这些新线程将拥有与创建线程一样的优先级。一个线程是守护线程当且仅当创建线程是守护线程。

补充:

        守护线程的概念:守护线程是用户线程的“保姆”,为其提供通用服务。当所有用户线程都退出了,只剩下守护线程,JVM就会退出。换言之,只要有用户线程在运行,JVM就不会退出。

Thread.sleep()

sleep()Thread的静态方法,使得线程睡眠指定时间,进入阻塞状态

Thread.join()

当前线程调用另一个线程的join方法当前线程执行结束进入阻塞状态另一个线程执行另一个线程执行结束当前线程进入就绪状态

以下例子可以实现“飞机”线程先执行,“高铁”线程执行,“火车”线程再执行的顺序

class Test {
    public static void main(String[] args) throws InterruptedException {
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();
        MyThread thread3 = new MyThread();
        thread1.setName("飞机");
        thread2.setName("火车");
        thread3.setName("高铁");

//        thread1启动,执行,结束后,当前线程thread1调用另一个线程thread1的join,
        thread1.start();
        thread1.join();

//        thread3启动,执行,结束后,当前线程thread3调用另一个线程thread3的join
        thread3.start();
        thread3.join();

        thread2.start();

    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            System.out.println(getName() +":" + i);
            try {
//                线程沉睡1000ms在执行
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

用户线程执行结束,守护线程也会直接消亡

class Test {
/**
实现张飞 关羽 刘备 同年同月同日死,
*/
    public static void main(String[] args) throws InterruptedException {
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();
        thread1.setName("关羽");
        thread2.setName("张飞");
        //设置main线程的name是刘备,
        Thread.currentThread().setName("刘备");
        for (int i = 1; i <= 10; i++) {
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
        //设置关羽和张飞是守护线程
        thread1.setDaemon(true);
        thread2.setDaemon(true);

        thread1.start();
        thread2.start();

    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(getName() + ":" + i);
        }
    }
}


线程同步与安全 synchronized

实现:多个窗口卖票

设计:

        多个窗口---多个线程

        卖票---卖的是同一沓票

多个线程操作同一个资源,需要用到synchronizeed

class Test {
    public static void main(String[] args){
        MyThread thread = new MyThread();
        Thread window1 = new Thread(thread, "窗口1");
        Thread window2 = new Thread(thread, "窗口2");
        Thread window3 = new Thread(thread, "窗口3");

        window1.start();
        window2.start();
        window3.start();
    }
}

class MyThread implements Runnable {
    private int ticketNum = 20;
    @Override
    public void run() {
        while (true) {
            synchronized (this) {
                if (ticketNum > 0) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ": 售出第" + ticketNum + "张票");
                    ticketNum--;
                }
            }
        }
    }
}

线程同步与安全 Lock

为什么要有Lock呢?synchronized无法看出来在哪里加锁在哪里释放锁,Lock提供的lock可以获取锁,unlock可以释放锁

Lock是一个接口,一般我们用他的实现类ReentrantLock

注意:释放锁unlock()最好放在finally中, 避免当前线程运行错误,无法释放锁, 而造成死锁

class YourThread implements Runnable {
    private static int ticketNum = 20;
    //    Lock是一个接口,无法被实例化,我们这里用他的实现类ReentrantLock
    Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
//            获取锁
            try {
                lock.lock();
                if (ticketNum > 0) {
                    if (ticketNum % 2 == 0) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + ": 售出第" + ticketNum + "张票 一等座");
                        ticketNum--;
                    } else {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + ": 售出第" + ticketNum + "张票 二等座");
                        ticketNum--;
                    }
                }
            } finally {
//                释放锁,建议放在finally中,避免当前线程运行错误,无法释放锁, 而造成死锁
                lock.unlock();
            }
        }
    }
}

多线程生产者消费者wait() notify() notifyAll()

笔面试常问:

       wait() notify() notifyAll()是Object提供的,而sleep() 和 join() 是Thread提供的。

Object.wait() 与 Thread.sleep() 的异同

两者最主要的区别在于:sleep() 方法没有释放锁,而 wait() 方法释放了锁 。

1、两者都可以暂停线程的执行。

2、wait() 通常被用于线程间交互/通信,sleep()通常被用于暂停执行。

3、wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify()或者 notifyAll() 方法。sleep()方法执行完成后,线程会自动苏醒。或者可以使用 wait(long timeout) 超时后线程会自动苏醒

实现:你预定了30天的牛奶,送奶工人每天会将一瓶牛奶送到你门口的奶箱。当牛奶送达后,你可以去拿走牛奶去喝。

送奶工人:生产者;奶箱:缓冲区;你:消费者

class Test {
    public static void main(String[] args) {
        TmpCache tmpCache = new TmpCache();
        Producer producer = new Producer(tmpCache);
        Consumer consumer = new Consumer(tmpCache);
        Thread producer1 = new Thread(producer, "producer");
        Thread consumer1 = new Thread(consumer, "consumer");

        producer1.start();
        consumer1.start();
    }
}

//暂存区中有生产和消费两个方法
class TmpCache {
    //    标注有无牛奶  一开始的时候是没有牛奶的  这里假设暂存区一次只能放一瓶牛奶
    private boolean state = false;


    public synchronized void put(int num) {
        if (state) { // 暂存区已经达到最大容量,就不能put了
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 否则可以put,milk的数量增加
        System.out.println("存入第" + num + "瓶牛奶");
        state = true;
        notify();
    }

    //    对于get,没有牛奶就不能执行,只能等有牛奶的时候才能取
    public synchronized void get(int num) {
        if (!state) { // 如果暂存区没有牛奶,就不能get
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("取出第" + num + "瓶牛奶");
        state = false;
        notify();
    }
}

//生产者
class Producer implements Runnable {
    TmpCache tmpCache;

    public Producer(TmpCache tmpCache) {
        this.tmpCache = tmpCache;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 30; i++) {
            tmpCache.put(i);
        }
    }
}

//消费者
class Consumer implements Runnable {
    TmpCache tmpCache;

    public Consumer(TmpCache tmpCache) {
        this.tmpCache = tmpCache;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 30; i++) {
            tmpCache.get(i);
        }
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值