Java_多线程01_多线程

多线程(理解)

 

要想了解多线程,必须先了解线程,而要想了解线程,必须先了解进程,因为线程是依赖于进程而存在。

 

一、    什么是进程?

通过任务管理器我们就看到了进程的存在。

就是正在运行的程序。是系统进行资源分配和调用的独立单位。

每一个进程都有它自己的内存空间和系统资源。

二、    多进程有什么意义呢?

单进程的计算机只能做一件事情,而我们现在的计算机都可以做多件事情。

举例:一边玩游戏(游戏进程),一边听音乐(音乐进程)。

现在的计算机都是支持多进程的,可以在一个时间段内执行多个任务。

可以提高CPU的使用率

问题:

一边玩游戏,一边听音乐是同时进行的吗?

不是。因为单CPU在某一个时间点上只能做一件事情。而我们在玩游戏,或者听音乐的时候,是CPU在做着程序间的高效切换让我们觉得是同时进行的。

三、    什么是线程呢?

在同一个进程内又可以执行多个任务,而这每一个任务我就可以看出是一个线程。

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

单线程:一个进程只有一条执行路径。

多线程:一个进程有多条执行路径。

四、    多线程有什么意义呢?

多线程的存在,不是提高程序的执行速度。其实是为了提高应用程序的使用率

程序的执行其实都是在抢CPU的资源,CPU的执行权。

多个进程是在抢这个资源,而其中的某一个进程如果执行路径比较多,就会有更高的几率抢到CPU的执行权。

我们是不敢保证哪一个线程能够在哪个时刻抢到,所以线程的执行有随机性。

五、    并行和并发。

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

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

六、    Java程序的运行原理及JVM的启动是多线程的吗?

A:Java命令会启动JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个 "主线程", 然后主线程去调用某个类的 main 方法。所以 main方法运行在主线程中。在此之前的所有程序都是单线程的。

B:JVM的启动是多线程的,因为它最低有两个线程启动了,主线程和垃圾回收线程。

七、    多线程的实现方案(自己补齐步骤及代码    掌握)

由于线程是依赖进程而存在的,所以我们应该先创建一个进程出来。而进程是由系统创建的,所以我们应该去调用系统功能创建一个进程。Java是不能直接调用系统功能的,所以,我们没有办法直接实现多线程程序。但是呢?Java可以去调用C/C++写好的程序来实现多线程程序。由C/C++去调用系统功能创建进程,然后由Java去调用这样的东西,然后提供一些类供我们使用。我们就可以实现多线程程序了。

那么Java提供的类是什么呢? Thread类

构造方法:

public Thread(Runnable target):分配新的 Thread 对象。

public Thread(Runnable target, String name):分配新的 Thread 对象。

成员方法:

public final String getName():返回该线程的名称。

public final void setName(String name):改变线程名称,使之与参数 name 相同。

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

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

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

public static void sleep(long millis):在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),

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

public static void yield():暂停当前正在执行的线程对象,并执行其他线程。

public final void setDaemon(boolean on):将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。该方法必须在启动线程前调用。

public final void stop():强迫线程停止执行。过时了,但可以使用,不建议。

public void interrupt():中断线程。

public static boolean interrupted():测试当前线程是否已经中断。

 

通过查看API,我们知道了有两种方式实现多线程程序。

A:继承Thread类

步骤

1).    自定义类MyThread继承Thread类。

2).    MyThread类里面重写run()?

面试题:run()和start()的区别?

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

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

为什么是run()方法呢?

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

3).    创建对象

4).    启动线程

/*******************************************************************************************************************************************/

public class MyThreadDemo {

    public static void main(String[] args) {

        // 创建线程对象,无参构造+setXxx()

        // MyThread my1 = new MyThread();

        // MyThread my2 = new MyThread();

        // my1.setName("周润发");// 设置线程名称

        // my2.setName("刘德华");

        // my1.start();

        // my2.start();

        // my1.start();// IllegalThreadStateException:非法的线程状态异常。// 为什么呢?因为这个相当于是my1线程被调用了两次。而不是启动两个线程。

        

        //带参构造方法给线程起名字

        MyThread my1 = new MyThread("周润发AAA");

        MyThread my2 = new MyThread("刘德华BBB");

        MyThread my3 = new MyThread("梁朝伟CCC");

        

        //更改线程优先级,默认是5

        my1.setPriority(10);// 最大值,线程优先级高仅仅表示线程获取的 CPU时间片的几率高,但是要在次数比较多,或者多次运行的时候才能看到比较好的效果。

        my2.setPriority(1);// 最小值

        // 获取默认优先级

        System.out.println(my1.getPriority());

        System.out.println(my2.getPriority());

        System.out.println(my3.getPriority());

        //获取main方法所在的线程对象的名称

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

        

        my1.start();

        try {

            my1.join();// 等待该线程终止

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        my2.start();

        my3.start();

        

    }

}

public class MyThread extends Thread {

    public MyThread() {}

    public MyThread(String name){

        super(name);

    }

    @Override

    public void run() {

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

            System.out.println(getName() + ":" + x + ",日期:" + new Date());

            

//            try {

//                Thread.sleep(1000);// 线程休眠

//            } catch (InterruptedException e) {

//                e.printStackTrace();

//            }

            

            Thread.yield();// 暂停当前正在执行的线程对象,并执行其他线程。

        }

    }

}

/*******************************************************************************************************************************************/

public class ThreadDaemonDemo {

    public static void main(String[] args) {

        ThreadDaemon td1 = new ThreadDaemon();

        ThreadDaemon td2 = new ThreadDaemon();

 

        td1.setName("关羽");

        td2.setName("张飞");

 

        // 设置守护线程

        td1.setDaemon(true);

        td2.setDaemon(true);

 

        td1.start();

        td2.start();

 

        Thread.currentThread().setName("刘备");

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

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

        }

    }

}

public class ThreadDaemon extends Thread {

    @Override

    public void run() {

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

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

        }

    }

}

/*******************************************************************************************************************************************/

/*

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

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

*/

public class ThreadStopDemo {

    public static void main(String[] args) {

        ThreadStop ts = new ThreadStop();

        ts.start();

 

        // 你超过三秒不醒过来,我就干死你

        try {

            Thread.sleep(3000);

            // ts.stop();

            ts.interrupt();

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

    }

}

public class ThreadStop extends Thread {

    @Override

    public void run() {

        System.out.println("开始执行:" + new Date());

 

        // 我要休息10秒钟,亲,不要打扰我哦

        try {

            Thread.sleep(10000);

        } catch (InterruptedException e) {

            // e.printStackTrace();

            System.out.println("线程被终止了");

        }

 

        System.out.println("结束执行:" + new Date());

    }

}

/*******************************************************************************************************************************************/

B:实现Runnable接口

步骤:

1).    自定义类MyRunnable实现Runnable接口

2).    重写run()方法

3).    创建MyRunnable类的对象

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

 

/*******************************************************************************************************************************************/

public class MyRunnableDemo {

    public static void main(String[] args) {

        // 创建MyRunnable类的对象

        MyRunnable my = new MyRunnable();

 

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

        // Thread(Runnable target)

        // Thread t1 = new Thread(my);

        // Thread t2 = new Thread(my);

        // t1.setName("周润发AAA");

        // t2.setName("刘德华BBB");

 

        // Thread(Runnable target, String name)

        Thread t1 = new Thread(my, "周润发AAA");

        Thread t2 = new Thread(my, "刘德华BBB");

 

        t1.start();

        t2.start();

    }

}

public class MyRunnable implements Runnable {

    @Override

    public void run() {

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

            // 由于实现接口的方式就不能直接使用Thread类的方法了,但是可以间接的使用

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

        }

    }

}

/*******************************************************************************************************************************************/

八、    线程的调度和优先级问题

        A:线程的调度

            a:分时调度

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

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

            a:默认是5

            b:范围是1-10

九、    线程的控制(常见方法)

        A:休眠线程

        B:加入线程

        C:礼让线程

        D:后台线程

        E:终止线程(掌握)

十、    线程的生命周期

        A:新建

        B:就绪

        C:运行

        D:阻塞

        E:死亡

 

线程生命周期图解:

 

十一、    电影院卖票程序的实现

        A:继承Thread类

        B:实现Runnable接口

/*******************************************************************************************************************************************/

/*

* 某电影院目前正在上映贺岁大片,共有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();

    }

}

public class SellTicket extends Thread {

    // 定义100张票

    // private int tickets = 1;

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

    private static int tickets = 1;

 

    @Override

    public void run() {

        // 定义100张票

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

        // int tickets = 100;

 

        // 是为了模拟一直有票

        while (true) {

            if (tickets <= 100) {

                try {

                    Thread.sleep(100);// 加入了休眠100毫秒,程序会有问题。

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

                System.out.println(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();

    }

}

public class SellTicket implements Runnable {

    // 定义100张票

    private int tickets = 1;

 

    @Override

    public void run() {

        while (true) {

            if (tickets <= 100) {

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

            }

        }

    }

}

/*******************************************************************************************************************************************/

十二、    电影院卖票程序出问题

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

        B:卖票问题

            a:同票多次

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

    }

}

public class SellTicket implements Runnable {

    // 定义100张票

    private int tickets = 1;

 

    @Override

    public void run() {

        while (true) {

            if (tickets <= 100) {

                try {

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

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

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

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

                //窗口2正在出售第101张票,tickets=102

                //窗口3正在出售第102张票,tickets=103

            }

        }

    }

}

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

        A:是否有多线程环境

        B:是否有共享数据

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

十四、    Java同步机制解决线程安全问题

同步的前提:

多个线程

解决问题的时候要注意:

个线程使用的是同一个锁对象

同步的好处

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

同步的弊端

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

        A:同步代码块

            synchronized(对象) {

                需要被同步的代码;

            }

 

1).    对象是什么呢?

我们可以随便创建一个对象试试。

2).    需要同步的代码是哪些呢?

把多条语句操作共享数据的代码的部分给包起来

注意:

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

            这里的锁对象可以是任意对象。多个线程必须是同一把锁

public class SellTicket implements Runnable {

    // 定义100张票

    private int tickets = 1;

    // 创建锁对象,定义同一把锁。

    private Object obj = new Object();

 

//    @Override

//    public void run() {

//        while (true) {

//            synchronized (new Object()) {// 不是同一把锁

//                if (tickets <= 100) {

//                    try {

//                        Thread.sleep(100);

//                    } catch (InterruptedException e) {

//                        e.printStackTrace();

//                    }

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

//                }

//            }

//        }

//    }

 

    @Override

    public void run() {

        while (true) {

            synchronized (obj) {

                if (tickets <= 100) {

                    try {

                        Thread.sleep(100);

                    } catch (InterruptedException e) {

                        e.printStackTrace();

                    }

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

                }

            }

        }

    }

}

        B:同步方法

            把同步加在方法上。

            

            这里的锁对象是this

 

此处缺少代码...

 

        C:静态同步方法

            把同步加在方法上。

            

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

 

此处缺少代码...

 

十五、    回顾以前的线程安全的类

        A:StringBuffer

        B:Vector

        C:Hashtable

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

            用Collections工具类的方法即可。

public class ThreadDemo {

    public static void main(String[] args) {

        // 线程安全的类

        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>()); // 线程安全

    }

}

 

 

 

猜你喜欢

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