Java_多线程02_多线程(高级)

多线程(理解)

一、    JDK5以后的针对线程的锁定操作和释放操作

虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁释放锁,JDK5以后提供了一个新的锁对象Lock。

Lock是接口,使用实现类ReentrantLock。

void lock():获取锁。

void unlock():释放锁。

 

public class SellTicketDemo {

    public static void main(String[] args) {

        // 创建资源对象

        SellTicket st = new SellTicket();

 

        // 创建三个窗口

        Thread t1 = new Thread(st, "窗口1");

        Thread t2 = new Thread(st, "窗口2");

        Thread t3 = new Thread(st, "窗口3");

 

        // 启动线程

        t1.start();

        t2.start();

        t3.start();

    }

}

public class SellTicket implements Runnable {

    // 定义票

    private int tickets = 1;

    // 定义锁对象

    private Lock lock = new ReentrantLock();

 

    @Override

    public void run() {

        while (true) {

            try {

                lock.lock();// 加锁

                if (tickets <= 100) {

                    try {

                        Thread.sleep(100);

                    } catch (InterruptedException e) {

                        e.printStackTrace();

                    }

                    System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets++) + "张票");

                }

            } finally {

                lock.unlock();// 释放锁

            }

        }

    }

}

二、    死锁问题的描述和代码体现

同步弊端:

    效率低

    如果出现了同步嵌套,就容易产生死锁问题

死锁问题及其代码

    是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象

    同步代码块的嵌套案例

 

public class DieLockDemo {

    public static void main(String[] args) {

        DieLock dl1 = new DieLock(true);

        DieLock dl2 = new DieLock(false);

 

        dl1.start();

        dl2.start();

    }

}

public class DieLock extends Thread {

    private boolean flag;

    public DieLock(boolean flag) {

        this.flag = flag;

    }

 

    @Override

    public void run() {

        if (flag) {

            synchronized (MyLock.objA) {

                System.out.println("if objA");

                synchronized (MyLock.objB) {

                    System.out.println("if objB");

                }

            }

        } else {

            synchronized (MyLock.objB) {

                System.out.println("else objB");

                synchronized (MyLock.objA) {

                    System.out.println("else objA");

                }

            }

        }

    }

}

public class MyLock {

    // 创建两把锁对象

    public static final Object objA = new Object();

    public static final Object objB = new Object();

}

 

三、    生产者和消费者多线程体现(线程间通信问题)

针对同一个资源的操作有不同种类的线程

通过设置线程(生产者)和获取线程(消费者)针对同一个学生对象进行操作

        

        资源类:Student

        设置数据类:SetThread(生产者)

        获取数据类:GetThread(消费者)

        测试类:StudentDemo

        

        代码:

            A:最基本的版本,只有一个数据。

            B:改进版本,给出了不同的数据,并加入了同步机制。

            C:等待唤醒机制改进该程序,让数据能够实现依次的出现

                wait()

                notify()

                notifyAll() (多生产多消费)

            D:等待唤醒机制的代码优化。把数据及操作都写在了资源类中。把同步代码块改进为同步方法实现。最终版代码

 

线程的状态转换图及常见执行情况:

 

四、    线程组:Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。

 

public class ThreadGroupDemo {

    public static void main(String[] args) {

//        method1();

 

        // 我们如何修改线程所在的组呢?

        // 创建一个线程组

        // 创建其他线程的时候,把其他线程的组指定为我们自己新建线程组

        method2();

    }

 

    private static void method2() {

        // ThreadGroup(String name)

        ThreadGroup tg = new ThreadGroup("这是一个新的组");

 

        MyRunnable my = new MyRunnable();

        // Thread(ThreadGroup group, Runnable target, String name):给线程设置分组

        Thread t1 = new Thread(tg, my, "林青霞");

        Thread t2 = new Thread(tg, my, "刘意");

        

        System.out.println(t1.getThreadGroup().getName());

        System.out.println(t2.getThreadGroup().getName());

        

        //通过组名称设置后台线程,表示该组的线程都是守护线程

        tg.setDaemon(true);

    }

 

    private static void method1() {

        MyRunnable my = new MyRunnable();

        Thread t1 = new Thread(my, "林青霞");

        Thread t2 = new Thread(my, "刘意");

        

        // 我不知道他们属于哪个线程组,我想知道,怎么办

        // 线程类里面的方法:public final ThreadGroup getThreadGroup()

        ThreadGroup tg1 = t1.getThreadGroup();

        ThreadGroup tg2 = t2.getThreadGroup();

        

        // 线程组里面的方法:public final String getName()

        String name1 = tg1.getName();

        String name2 = tg2.getName();

        

        System.out.println(name1);

        System.out.println(name2);

        // 通过结果我们知道了:默认情况下,所有的线程都属于主线程组。

        

        // 通过下面的测试,你应该能够看到,默任情况下,所有的线程都属于同一个组

        System.out.println(Thread.currentThread().getThreadGroup().getName());

    }

}

public class MyRunnable implements Runnable {

    @Override

    public void run() {

        for (int x = 0; x < 100; x++) {

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

        }

    }

}

五、    线程池

程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池

JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法

public static ExecutorService newCachedThreadPool():创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。

public static ExecutorService newFixedThreadPool(int nThreads):创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。

public static ExecutorService newSingleThreadExecutor():创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。(

这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。它提供了如下方法

Future<?> submit(Runnable task):提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。该 Future 的 get 方法在成功 完成时将会返回 null。

<T> Future<T> submit(Callable<T> task):提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future。该 Future 的 get 方法在成功完成时将会返回该任务的结果。

案例演示

创建线程池对象

创建Runnable实例

提交Runnable实例

关闭线程池

六、    多线程实现的第三种方案

七、    多线程的面试题

 

 

 

 

猜你喜欢

转载自www.cnblogs.com/zhaolanqi/p/9289820.html