zoukankan      html  css  js  c++  java
  • Java多线程解析

    Java多线程解析

    1.1   程序/进程

    什么是程序?

    程序就是一堆静止的代码。

    什么是进程?

    进程就是程序的一次执行。

     

    1.2   操作系统的分类

    单任务OS

    一段时间内只能执行一个任务,如果要执行其他任务,必须把前一个任务结束后,才能进行第二个任务

     

    代表:DOS

     

     

    多任务OS

    一段时间可以运行多个任务(程序),比如有两个任务A,B,当前运行A任务,如果想运行B任务,可以把A任务切换出来,让B运行。

    在某个时刻只能有一个任务运行。

    进程的概念和并发的概念

     

    在单CPU的情况下,多个任务产生多个进程,这些进程之间可以切换,通过CPU时间片轮转来达到多任务。

    并发:多个进程在一段时间内运行,叫做进程的并发。

     

    并行:多个进程在同一时刻运行,叫做进程的并行。

    一定个多个CPU才能并行。

    进程提高了CPU的利用率。

    线程

    线程是把进程中的执行单元进行细分,分为能够独立运行的功能体,这些功能体就叫做线程

    线程进一步提高了CPU的利用率

    案例:

    把学生上课比作学习的进程

     

    总结

    多线程进一步提高了CPU的利用率

    多线程增加的程序的复杂度

    1.3   进程和线程的区别和联系

     

    可参考文章:

    https://www.cnblogs.com/geeta/p/9474051.html

    1.4   实现线程的方式

    1.4.1     继承Thread

    public class MyThread extends Thread{

       

        @Override

        public void run() {

            for(int i=0;i<10;i++) {

                System.out.println("MyThread:"+i);

            }

        }

    }

    public class Test01 {

        public static void main(String[] args) {

           

            // 【1】创建一个线程

            MyThread t1 = new MyThread();

            // 【2】启动线程,不是调用run

            t1.start();

     

        }

    }

     

    注意:

    [1]启动线程通过start() 方法

    [2] 一个进程中一定有一个主线程,一般是main方法所在的线程。

    [3] 从执行结果看,具有多线程的程序,程序执行结果不确定 => 增加了程序控制的复杂性

    分析线程执行轨迹

     

    1.4.2     实现Runnable接口

    public class MyRun implements Runnable {

     

        @Override

        public void run() {

            for(int i=0;i<10;i++) {

                System.out.println("MyThread:->"+i);

            }

        }

    }

    public class TestRun {

        public static void main(String[] args) {

           

            MyRun run = new MyRun();

            Thread t1 = new Thread(run);

            t1.start();

           

            for(int i=0;i<10;i++) {

                System.out.println("MainThread:=>"+i);

            }

        }

    }

     

    实现Runnable接口表示类具备多线程运行的能力。

    1.4.3     两种实现方式的对比

    从形式上

    一个类继承了Thread类后就不能再继承其他类。

    一个类实现了Runnable接口,还可以继承其他类。

    从本质上

    实现Runnable接口的类可以给多个线程 资源共享

    案例:请模拟火车站购票的案例

    通过继承Thead实现

    public class ThreadTicket extends Thread{

       

        private static int ticket = 5;

       

        public ThreadTicket(String aName) {

            super(aName);

        }

       

        @Override

        public void run() {

            for(int i=0;i<10;i++) {

                if(ticket > 0) {

                    ticket--;

                    System.out.println(this.getName()+"还剩"+ticket+"张票");

                }

            }

        }

    }

    public class TestTicket {

        public static void main(String[] args) {

           

            ThreadTicket t1 = new ThreadTicket("窗口1");

            ThreadTicket t2 = new ThreadTicket("窗口2");

            ThreadTicket t3 = new ThreadTicket("窗口3");

            ThreadTicket t4 = new ThreadTicket("窗口4");

           

            t1.start();

            t2.start();

            t3.start();

            t4.start();

        }

    }

     

    通过Runnable接口实现

    public class RunTicket implements Runnable {

     

        private int ticket = 5;

       

        @Override

        public void run() {

            for(int i=0;i<10;i++) {

                if(ticket > 0) {

                    ticket--;

                    System.out.println(Thread.currentThread().getName() + "还剩"+ticket+"张票");

                }

            }

        }

    }

    public class TestRun {

        public static void main(String[] args) {

           

            RunTicket run = new RunTicket();

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

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

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

            Thread t4 = new Thread(run,"窗口4");

           

            t1.start();

            t2.start();

            t3.start();

            t4.start();

           

        }

    }

    分析多线程运行轨迹

     

    1.5   多线程和代理设计模式

     

    1.6   线程的生命周期

     

    1.7   线程的常用方法

    1.7.1     [1]获取当前线程

    public static void main(String[] args) {

            // 返回当前正在执行的线程的对象

            Thread mainThread = Thread.currentThread();

            System.out.println("当前线程的名称:"+mainThread.getName());

            System.out.println("当前线程的Id:"+mainThread.getId());

        }

    1.7.2     [2]线程优先级

    Thread中定义三个静态字段表示优先级

    优先级字段

    MAX_PRIORITY

    10

    MIN_PRIORITY

    1

    NORM_PRIORITY

    5

    MyThread02 t1 = new MyThread02("t1");

            t1.setPriority(6);

            t1.start();

           

            MyThread02 t2 = new MyThread02("t2");

            t2.setPriority(Thread.MAX_PRIORITY);

            t2.start();

    结论

    [1]优先级高不一定先运行。

    [2]优先级高说明线程有可能被最先调度

    1.7.3     [3]测试线程的活动状态

    使用Thread类中的isAlive()方法,判断线程的状态

    活动状态的界定:[调用start后,未死亡]

    public static void main(String[] args) {

            MyThread03 t1 = new MyThread03();

            System.out.println(t1.isAlive());

            t1.start();

           

            System.out.println(t1.isAlive());

        }

    1.7.4     [4]线程的强制执行

    Join方法

    调用该方法的线程强制执行,其它线程处于阻塞状态,该线程执行完毕后,其它线程再执行

    => 其他线程阻塞,直到该线程执行完成。

    public static void main(String[] args) {

     

            MyThread04 t1 = new MyThread04("t1");

            t1.start();

     

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

                if(i == 3) {

                    try {

                        t1.join();

                    } catch (InterruptedException e) {

                        e.printStackTrace();

                    }

                }else {            

                    System.out.println(Thread.currentThread().getName() + "=>" + i);

                }

            }

        }

    1.7.5     [5]线程的休眠

    1.7.6     [6]线程的礼让

    public static void main(String[] args) {

           

            MyThread06 t1 = new MyThread06();

            t1.start();

           

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

                if(i==3) {

                    // 线程礼让

                    Thread.yield();

                }else {            

                    System.out.println(Thread.currentThread().getName()+"=>"+i);

                }

            }

        }

    Thread-0->0

    main=>0

    Thread-0->1

    main=>1

    Thread-0->2

    main=>2   ----àmain线程礼让一次

    Thread-0->3   -----à主线程礼让一次,礼让给t1执行。

    main=>4

    Thread-0->4

     

    注意:

    [1]礼让的线程进入就绪状态。

    [2]线程礼让完成后,各个线程又开始抢占CPU。

    其中的一种特殊情况

    main=>0

    Thread-0->0

    Thread-0->1

    main=>1

    main=>2    -----à main开始礼让

    main=>4

    Thread-0->2

    Thread-0->3

    Thread-0->4

     

    API解析

    A hint to the scheduler that the current thread is willing to yield its current use of a processor. The scheduler is free to ignore this hint.

    1.7.7     [7]线程中断

    public class MyThread07 extends Thread{

        @Override

        public void run() {

            System.out.println("[1] 线性开始执行");

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

                try {

                    Thread.sleep(1000);

                    System.out.println("[2] 线程休眠正常结束");

                } catch (InterruptedException e) {

                    System.out.println("[3] 线程被中断");

                    break;

                    // e.printStackTrace();

                }

            }

           

            System.out.println("[4]线程结束");

        }

    }

    public class Test07Interrupt {

        public static void main(String[] args) {

           

            MyThread07 t1 = new MyThread07();

            t1.start();

           

            try {

                Thread.sleep(2000);

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

           

            t1.interrupt();

        }

    }

     

    总结

    [1]终止线程的方法stop已经不建议使用,容易导致死锁。

    [2]一般可以通过中断线程的方法iterrupt中断线程的执行,让线程提前结束。提前让线程结束是一种正常的结束。

    1.8   线程安全

    多线程虽然提高了CPU的利用率,但是导致严重的数据错乱的问题。

    窗口1还剩5张票

    窗口2还剩5张票

    窗口2还剩3张票

    窗口2还剩2张票

    窗口1还剩4张票

    窗口2还剩1张票

    同步的概念

    同步让一个逻辑单元执行完成。例如买票系统中把

    if(ticket > 0) {

         System.out.println(this.getName()+"还剩"+ticket+"张票");

         ticket--;

    }

    定义成逻辑单元(一个整体)。

    逻辑单元要么都执行,要么都不执行

    如何保证?è 引入同步的概念保证逻辑单元的统一执行。

    1.8.1     [1]同步代码块

    // 同步代码块

    // mutex(互斥元,互斥体;互斥量) ==> 互斥锁

    synchronized (mutex) {            

        if(ticket > 0) {

            System.out.println(this.getName()+"还剩"+ticket+"张票");

            ticket--;

        }

    }

    [1]把逻辑单元放到同步代码块中

    [2]给同步代码块加互斥锁

    互斥锁一定要是一个对象;互斥锁最好要选择共享资源作为互斥锁

    1.8.2     [2]同步方法

    当逻辑单元过于复杂时,可以选择使用同步方法,使用synchronized修饰

    public class MyRun implements Runnable{

     

        private int ticket = 10;

       

        @Override

        public void run() {

            for(int i=0;i<20;i++) {

                this.buyTicket();

            }

        }

       

        public synchronized void buyTicket() {

            if(ticket>0) {

               

                System.out.println(Thread.currentThread().getName()+"还剩"+ticket+"张票");

               

                try {

                    // 模拟买票的过程

                    Thread.sleep(1000);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

               

                ticket--;

            }

        }

     

    }

    同步总结

     

    1.8.3     死锁(B)

    同步保证多线程访问共享资源时数据不会错乱,但是过多的同步会导致死锁。

    当线程t1需要访问A资源,把A加锁,同时还需要B资源,t1也将B资源加锁,而B资源被线程t2加锁,而t2又需要A资源,结果导致t1,t2陷入相互等待的过程。

    public class MyThread01 extends Thread{

       

        private Object A;

        private Object B;

       

        public MyThread01(Object a,Object b) {

            super();

            this.A = a;

            this.B = b;

        }

       

        @Override

        public void run() {

           

            synchronized (A) {

               

                System.out.println("t1用于A,然后申请B资源");

               

                synchronized (B) {

                    System.out.println("t1用于A,申请到B资源");

                }

            }

           

            System.out.println("t1正常结束...");

        }

    }

    public class MyThread02 extends Thread{

       

        private Object A;

        private Object B;

       

        public MyThread02(Object a,Object b) {

            super();

            this.A = a;

            this.B = b;

        }

       

        @Override

        public void run() {

           

            synchronized (B) {

               

                System.out.println("t2拥有B资源,然后申请A资源");

                synchronized (A) {

                    System.out.println("t2拥有B资源,申请到A资源");

                }

            }

           

            System.out.println("t2正常结束...");

        }

    }

    public class Test {

        public static void main(String[] args) {

           

            // 创建两个资源A,B

            Object A = new Object();

            Object B = new Object();

           

            MyThread01 t1 = new MyThread01(A, B);

            //MyThread01 t2 = new MyThread01(A, B);

            MyThread02 t2 = new MyThread02(A, B);

           

            t1.start();

            t2.start();

        }

    }

    死锁一般情况下表示互相等待,是程序运行时出现的一种问题!

    实际案例:买饭

    1.9   生产者和消费者问题

    package sxt06.thread07;

     

    public class GoodsStore {

     

        private String brand;

        private String name;

       

        // 声明一个标识,标识仓库中是否有商品

        private boolean hasGoods = false;

     

        public String getBrand() {

            return brand;

        }

     

        public void setBrand(String brand) {

            this.brand = brand;

        }

     

        public String getName() {

            return name;

        }

     

        public void setName(String name) {

            this.name = name;

        }

     

        /**

         * 生产者生产商品

         * @param brand

         * @param name

         */

        public synchronized void push(String brand, String name) {

           

            if(hasGoods) {

                try {

                    // 当前线程等待

                    super.wait();

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

           

            this.setBrand(brand);

           

            try {

                Thread.sleep(1000);

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

           

            this.setName(name);

            System.out.println("生产了-->"+brand+name);

           

            hasGoods = true;

            super.notify();

        }

     

        /**

         * 消费者取商品

         */

        public synchronized void get() {

           

            if(!hasGoods) {

                try {

                    super.wait();

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

           

            try {

                Thread.sleep(1000);

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

            System.out.println("消费了<--"+brand+name);

           

            hasGoods = false;

            super.notify();

        }

    }

    注意:

    以上方法都只能在同步方法或者同步代码块中使用,否则会抛出异常

    总结:

    在线程间通信过程中,要解决两个核心问题

    [1]线程同步问题。把程序的逻辑单元放到同步代码块和同步方法中

    [2]线程间通信问题。wait/notify


     

  • 相关阅读:
    SVN使用教程总结
    windows禅道环境搭建
    反编译.NET工程
    DAO层,Service层,Controller层、View层 的分工合作
    MyBatis在insert插入操作时返回主键ID的配置
    MySQL中You can't specify target table for update in FROM clause异常
    MySQL对时间戳的转换处理
    苹果台式一体机笔记本安装win双系统攻略教程
    Eclipse安装SVN教程
    java遇到 Check $M2_HOME 问题 解决-Dmaven.multiModuleProjectDirectory system property is not set. Check $M2_HOME environment variable and mvn script match.
  • 原文地址:https://www.cnblogs.com/eric666666/p/10719820.html
Copyright © 2011-2022 走看看