java学习之多线程篇

(1)多线程:一个应用程序有多条执行路径

        进程:正在执行的应用程序。正在运行的程序,是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和系统资源。

        线程:进程的执行单元,执行路径(是进程中的单个顺序控制流,是一条执行路径)

        单线程:一个应用程序只有一条执行路径

        多线程:一个应用程序有多条执行路径

        多进程的意义?

            提高CPU的使用率

        多线程的意义?

            提高应用程序的使用率

        并行和并发:

            并行 是逻辑上同时发生,指在某一个时间内同时运行多个程序。

            并发 是物理上同时发生,指在某一个时间点同时运行多个程序。

    (2)Java程序的运行原理及JVM的启动是多线程的吗?

        A:由java命令启动JVM,JVM启动就相当于启动了一个进程。接着有该进程创建了一个主线程去调用main方法。

        B:JVM的启动是多线程的,原因是垃圾回收线程也要先启动,否则很容易会出现内存溢出。现在的垃圾回收线程加上前面的主线程,

           最低启动了两个线程,所以,jvm的启动其实是多线程的。

    (3)多线程的实现方案(掌握)

        A:继承Thread类

           1. 步骤 :

          A:自定义类MyThread继承Thread类。

          B:MyThread类里面重写run()

              为什么是run()方法呢?

                不是类中的所有代码都需要被线程执行的。而这个时候,为了区分哪些代码能够被线程执行,java提供了Thread类中的  

                 run()用来包含那些被线程执行的代码

          C:创建对象     MyThread my1 = new MyThread();

          D:启动线程    my1.start();

          

          注意:

              1. run()和start()的区别?

                  run():仅仅是封装被线程执行的代码,直接调用是普通方法

                  start():首先启动了线程,然后再由jvm去调用该线程的run()方法。

               2.my1.start();  my1.start();  IllegalThreadStateException:非法的线程状态异常

                    因为这个相当于是my线程被调用了两次。而不是两个线程启动。

 2.获取/设置线程的名称的方法:

     public final String getName():获取线程的名称。

     public final void setName(String name):设置线程的名称

     public static Thread currentThread():返回当前正在执行的线程对象

     Thread.currentThread().getName()

          

        B:实现Runnable接口

             步骤:

          A:自定义类MyRunnable实现Runnable接口

          B:重写run()方法

          C:创建MyRunnable类的对象

          D:创建Thread类的对象,并把C步骤的对象作为构造参数传递

    (4)线程的调度和优先级问题

        A:线程的调度

            a:分时调度

            b:抢占式调度 (Java采用的是该调度方式)

        B:获取和设置线程优先级

               public final int getPriority():返回线程对象的优先级

               public final void setPriority(int newPriority):更改线程的优先级。

                 线程默认优先级是5。

       线程优先级的范围是:1-10。

       线程优先级高仅仅表示线程获取的 CPU时间片的几率高,但是要在次数比较多,或者多次运行的时候才能看到比较好的效果。

            IllegalArgumentException:非法参数异常。抛出的异常表明向方法传递了一个不合法或不正确的参数。

    (5)线程的控制(常见方法)

        A:休眠线程

               public static void sleep(long millis)

        B:加入线程

                public final void join():等待该线程终止。

        C:礼让线程

               public static void yield():暂停当前正在执行的线程对象,并执行其他线程。让多个线程的执行更和谐,但是不能靠它保证一人一次。

        D:后台线程

               public final void setDaemon(boolean on):将该线程标记为守护线程或用户线程。

               当正在运行的线程都是守护线程时,Java 虚拟机退出。 该方法必须在启动线程前调用。

        E:终止线程(掌握)

               public final void stop():让线程停止,过时了,但是还可以使用。

               public void interrupt():中断线程。 把线程的状态终止,并抛出一个InterruptedException。

    (6)线程的生命周期

        A:新建

        B:就绪

        C:运行

        D:阻塞

        E:死亡

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

   

(7)电影院卖票程序的实现

        A:继承Thread类


public class SellTicket extends Thread {

    // 定义100张票

    // private int tickets = 100;

    // 为了让多个线程对象共享这100张票,我们其实应该用静态修饰

    private static int tickets = 100;



    @Override

    public void run() {

        // 定义100张票

        // 每个线程进来都会走这里,这样的话,每个线程对象相当于买的是自己的那100张票,这不合理,所以应该定义到外面

        // int tickets = 100;



        // 是为了模拟一直有票

        while (true) {

            if (tickets > 0) {

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

            }

        }

    }

}



/*

 * 某电影院目前正在上映贺岁大片(红高粱,少林寺传奇藏经阁),共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票。

 * 继承Thread类来实现。

 */

public class SellTicketDemo {

    public static void main(String[] args) {

        // 创建三个线程对象

        SellTicket st1 = new SellTicket();

        SellTicket st2 = new SellTicket();

        SellTicket st3 = new SellTicket();



        // 给线程对象起名字

        st1.setName("窗口1");

        st2.setName("窗口2");

        st3.setName("窗口3");



        // 启动线程

        st1.start();

        st2.start();

        st3.start();

    }

}

        B:实现Runnable接口


public class SellTicket implements Runnable {

    // 定义100张票

    private int tickets = 100;



    @Override

    public void run() {

        while (true) {

            if (tickets > 0) {

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

                        + (tickets--) + "张票");

            }

        }

    }

}



/*

 * 实现Runnable接口的方式实现

 */

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();

    }

}

    (8)电影院卖票程序出问题

        A:为了更符合真实的场景,加入了休眠100毫秒。

        B:卖票问题

            a:同票多次

            b:负数票


public class SellTicket implements Runnable {

    // 定义100张票

    private int tickets = 100;



    @Override

    public void run() {

        while (true) {

            // t1,t2,t3三个线程

            // 这一次的tickets = 1;

            if (tickets > 0) {

                // 为了模拟更真实的场景,我们稍作休息

                try {

                    Thread.sleep(100); //t1进来了并休息,t2进来了并休息,t3进来了并休息,

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }



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

                        + (tickets--) + "张票");

                //窗口1正在出售第1张票,tickets=0

                //窗口2正在出售第0张票,tickets=-1

                //窗口3正在出售第-1张票,tickets=-2

            }

        }

    }

}



/*

 * 实现Runnable接口的方式实现

 *

 * 通过加入延迟后,就产生了连个问题:

 * A:相同的票卖了多次

 *         CPU的一次操作必须是原子性的

 * B:出现了负数票

 *         随机性和延迟导致的

 */

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();

    }

}

    (9)多线程安全问题的原因(也是我们以后判断一个程序是否有线程安全问题的依据)

        A:是否有多线程环境

        B:是否有共享数据

        C:是否有多条语句操作共享数据

    (10)同步解决线程安全问题

          把多条语句操作共享数据的代码给包成一个整体,让某个线程在执行的时候,别人不能来执行。

        A:同步代码块

            synchronized(对象) {

                需要被同步的代码;

            }

            这里的锁对象可以是任意对象。

            解决问题的时候要注意:多个线程必须是同一把锁。

        B:同步方法

            把同步加在方法上。

            这里的锁对象是this

        C:静态同步方法

            把同步加在方法上。

            这里的锁对象是当前类的字节码文件对象(反射)

       同步的好处

            同步的出现解决了多线程的安全问题。

       同步的弊端

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

            2.容易产生死锁


public class SellTicket implements Runnable {



    // 定义100张票

    private int tickets = 100;



    // 定义同一把锁

    private Object obj = new Object();



    @Override

    public void run() {

        while (true) {

            // t1,t2,t3都能走到这里

            // 假设t1抢到CPU的执行权,t1就要进来

            // 假设t2抢到CPU的执行权,t2就要进来,发现门是关着的,进不去。所以就等着。

            // 门(开,关)

            synchronized (obj) { // 发现这里的代码将来是会被锁上的,所以t1进来后,就锁了。(关)

                if (tickets > 0) {

                    try {

                        Thread.sleep(100); // t1就睡眠了

                    } catch (InterruptedException e) {

                        e.printStackTrace();

                    }

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

                            + "正在出售第" + (tickets--) + "张票 ");

                    //窗口1正在出售第100张票

                }

            } //t1就出来可,然后就开门。(开)

        }

    }

}

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();

    }

}

    (11)回顾以前的线程安全的类

        A:StringBuffer

        B:Vector

        C:Hashtable

        D:如何把一个线程不安全的集合类变成一个线程安全的集合类

            用Collections工具类的方法即可。


        // 线程安全的类

        StringBuffer sb = new StringBuffer();

        Vector<String> v = new Vector<String>();

        Hashtable<String, String> h = new Hashtable<String, String>();



        // Vector是线程安全的时候才去考虑使用的,但是我还说过即使要安全,我也不用你

        // 那么到底用谁呢?

        // public static <T> List<T> synchronizedList(List<T> list)

        List<String> list1 = new ArrayList<String>();// 线程不安全

        List<String> list2 = Collections

                .synchronizedList(new ArrayList<String>()); // 线程安全
发布了114 篇原创文章 · 获赞 52 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Smile_Sunny521/article/details/89704158