zoukankan      html  css  js  c++  java
  • 多线程(二)

    多线程(理解)
    (1)Lock锁

    为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock。

    Lock:
       void lock():  获取锁。
       void unlock():释放锁。
      ReentrantLock是Lock的实现类.

    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 = 100;
    
        // 定义锁对象
        private Lock lock = new ReentrantLock();
    
        @Override
        public void run() {
            while (true) {
                try {
                    // 加锁
                    lock.lock();
                    if (tickets > 0) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()
                                + "正在出售第" + (tickets--) + "张票");
                    }
                } finally {
                    // 释放锁
                    lock.unlock();
                }
            }
        }
    
    }

    (2)死锁问题的描述和代码体现

     同步的弊端:
       A:效率低;
       B:容易产生死锁.

      死锁:
       两个或两个以上的线程在争夺资源的过程中,发生的一种相互等待的现象。

    代码体现:

    public class MyLock {
        // 创建两把锁对象
        public static final Object objA = new Object();
        public static final Object objB = new Object();
    }
    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");
                    }
                }
            }
        }
    }


    (3)生产者和消费者多线程体现(线程间通信问题)
     以学生作为资源来实现的
      资源类:Student
      设置数据类:SetThread(生产者)
      获取数据类:GetThread(消费者)
      测试类:StudentDemo
     代码:
      A:最基本的版本,只有一个数据。
      B:改进版本,给出了不同的数据,并加入了同步机制。
      C:等待唤醒机制改进该程序,让数据能够实现依次的出现。
          wait()
          notify()
          notifyAll() (多生产多消费)
      D:等待唤醒机制的代码优化。把数据及操作都写在了资源类中。
    (4)线程组

      线程组: 把多个线程组合到一起。

        它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。

    构造方法:

    ThreadGroup(String name)
    Thread(ThreadGroup group, Runnable target, String name)

    (5)线程池

    线程池的好处:线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
      如何实现线程的代码呢?
     A:创建一个线程池对象,控制要创建几个线程对象。

     public static ExecutorService newFixedThreadPool(int nThreads)

     B:这种线程池的线程可以执行:
       可以执行Runnable对象或者Callable对象代表的线程
     做一个类实现Runnable接口。
     C:调用如下方法即可

     Future<?> submit(Runnable task)
       <T> Future<T> submit(Callable<T> task)

      D:我就要结束,可以吗?
         可以。

    实现代码:

    public class ExecutorsDemo {
        public static void main(String[] args) {
            // 创建一个线程池对象,控制要创建几个线程对象。
            // public static ExecutorService newFixedThreadPool(int nThreads)
            ExecutorService pool = Executors.newFixedThreadPool(2);
    
            // 可以执行Runnable对象或者Callable对象代表的线程
            pool.submit(new MyRunnable());
            pool.submit(new MyRunnable());
    
            //结束线程池
            pool.shutdown();
        }
    }

    (6)多线程实现的第三种方案

     1:创建一个线程池对象,控制要创建几个线程对象。

    public static ExecutorService newFixedThreadPool(int nThreads)

     2:这种线程池的线程可以执行:
       可以执行Runnable对象或者Callable对象代表的线程
     做一个类实现Runnable接口。
     3:调用如下方法即可

     Future<?> submit(Runnable task)
       <T> Future<T> submit(Callable<T> task)

     4:我就要结束,可以吗?
        可以。

    代码实现:

    import java.util.concurrent.Callable;
    
    //Callable:是带泛型的接口。
    //这里指定的泛型其实是call()方法的返回值类型。
    public class MyCallable implements Callable {
    
        @Override
        public Object call() throws Exception {
            for (int x = 0; x < 100; x++) {
                System.out.println(Thread.currentThread().getName() + ":" + x);
            }
            return null;
        }
    
    }
    public class CallableDemo {
        public static void main(String[] args) {
            //创建线程池对象
            ExecutorService pool = Executors.newFixedThreadPool(2);
            
            //可以执行Runnable对象或者Callable对象代表的线程
            pool.submit(new MyCallable());
            pool.submit(new MyCallable());
            
            //结束
            pool.shutdown();
        }
    }

     

    (7)匿名内部类实现多线程:

     匿名内部类的格式:
       new 类名或者接口名() {
         重写方法;
       };
     本质:是该类或者接口的子类对象。

    代码实现:

    public class ThreadDemo {
        public static void main(String[] args) {
            // 继承Thread类来实现多线程
            new Thread() {
                public void run() {
                    for (int x = 0; x < 100; x++) {
                        System.out.println(Thread.currentThread().getName() + ":"
                                + x);
                    }
                }
            }.start();
    
            // 实现Runnable接口来实现多线程
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int x = 0; x < 100; x++) {
                        System.out.println(Thread.currentThread().getName() + ":"
                                + x);
                    }
                }
            }) {
            }.start();
    
            // 更有难度的
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int x = 0; x < 100; x++) {
                        System.out.println("hello" + ":" + x);
                    }
                }
            }) {
                public void run() {
                    for (int x = 0; x < 100; x++) {
                        System.out.println("world" + ":" + x);
                    }
                }
            }.start();
        }
    }

    (8)多线程的面试题

    1:多线程有几种实现方案,分别是哪几种?
      答:两种。
      继承Thread类
      实现Runnable接口
      扩展一种:实现Callable接口。这个得和线程池结合。

     

    2:同步有几种方式,分别是什么?
      答:两种。
      同步代码块
      同步方法

     

    3:启动一个线程是run()还是start()?它们的区别?
     答:start();

     run():封装了被线程执行的代码,直接调用仅仅是普通方法的调用。
     start():启动线程,并由JVM自动调用run()方法。

     

    4:sleep()和wait()方法的区别
     答:sleep():必须指时间;不释放锁。
       wait():可以不指定时间,也可以指定时间;释放锁。

     

    5:为什么wait(),notify(),notifyAll()等方法都定义在Object类中
     答:因为这些方法的调用是依赖于锁对象的,而同步代码块的锁对象是任意锁。
       而Object代码任意的对象,所以,定义在这里面。

     

    6:线程的生命周期图

     

  • 相关阅读:
    centos7下安装Jdk8、Tomcat8
    CentOS7.3下使用YUM安装MySQL5.6
    数据库表直接生成Excel
    分页工具类
    ResponseUtil
    图片上传
    UUIDUtils
    html和js
    java
    Eclipse启动发生的错误An internal error occurred during: "Initializing Java Tooling".
  • 原文地址:https://www.cnblogs.com/crazypokerk/p/9128252.html
Copyright © 2011-2022 走看看