美文网首页码农的世界程序员
java进阶(二):多线程

java进阶(二):多线程

作者: 丁俊杰_ | 来源:发表于2018-10-02 21:56 被阅读4次

    1、多线程的目的

    即“最大限度的利用CPU资源”,当某一线程的处理不需要占用CPU而只和I/O等资源打交道时,让需要占用CPU资源的其他线程有机会获得CPU资源。

    2、创建线程

    方法一:
    通过继承Thread类创建线程

    • 普通线程如果继承自Thread类,就成为了一个线程类,并可以通过该类的start方法来启动线程,执行线程代码。
      *Thread类的子类可以直接实例化,但在子类中必须覆盖run方法才能真正运行线程代码。
    class MyThread extends Thread {
        private int i = 0;
        @Override
        public void run() {
            for (i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + " " + i);
            }
        }
    }
    
    public class ThreadTest {
        public static void main(String[] args) {
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + " " + i);
                if (i == 30) {
                    Thread myThread1 = new MyThread();     // 创建一个新的线程  myThread1  此线程进入新建状态
                    Thread myThread2 = new MyThread();     // 创建一个新的线程 myThread2 此线程进入新建状态
                    myThread1.start();                     // 调用start()方法使得线程进入就绪状态
                    myThread2.start();                     // 调用start()方法使得线程进入就绪状态
                }
            }
        }
    }
    

    方法二:
    通过实现Runnable接口创建线程

    • 实现Runnable接口的类必须借助Thread类才能创建线程。通过Runnable接口创建线程分为两步:
      1、创建实现Runnable接口的类的实例。
      2、创建一个Thread类对象,将第一步得到的实例作为参数传入Thread类的构造方法。
    • 通过Tread类的start方法启动线程。
    class MyRunnable implements Runnable {
        private int i = 0;
        @Override
        public void run() {
            for (i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + " " + i);
            }
        }
    }
    
    public class ThreadTest {
    
        public static void main(String[] args) {
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + " " + i);
                if (i == 30) {
                    Runnable myRunnable = new MyRunnable(); // 创建一个Runnable实现类的对象
                    Thread thread1 = new Thread(myRunnable); // 将myRunnable作为Thread target创建新的线程
                    Thread thread2 = new Thread(myRunnable);
                    thread1.start(); // 调用start()方法使得线程进入就绪状态
                    thread2.start();
                }
            }
        }
    }
    

    当然,还可以直接使用匿名内部类,方法十分简便,仅需重写run方法,例如:

    /*
     * 匿名内部类的格式:
     */
    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();
    
            // 更有难度的,在Thread匿名内部类的里面再一次重写run方法
            //在实际运行时的结果是 hello+x。以thread的run方法为准。但是此处无意义
            new Thread(new Runnable() {
                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    for (int x = 0; x < 100; x++) {
                        System.out.println("java" + "--" + x);
                    }
    
                }
            }) {
                public void run() {
                    for (int x = 0; x < 100; x++) {
                        System.out.println("hello" + "--" + x);
                    }
                }
            }.start();
        }
    }
    

    3、线程完整生命周期

    生命周期


    4、线程同步

    线程同步是为了防止多个线程访问一个数据对象时,对数据造成破坏。

    注意:用synchronized关键字声明的静态方法,同时只能被一个执行线程访问,但是其他线程可以访问这个对象的非静态方法。即:两个线程可以同时访问一个对象的两个不同的synchronized方法,其中一个是静态方法,一个是非静态方法。
    //示例
    public synchronized void addAmount(double amount) {
    }
    
    synchronized(this){
    }
    

    5、线程通信

    假设有这么一个问题,要求编写两个线程,一个线程打印125,另一个线程打印字母AZ,打印顺序为12A34B56C……5152Z,这就要求使用线程间的通信,解决的方法有以下几种:

    • 使用一个共享变量控制,利用最基本的synchronized、notify、wait
    public class MethodOne {
        private final ThreadToGo threadToGo = new ThreadToGo();
        public Runnable newThreadOne() {
            final String[] inputArr = Helper.buildNoArr(52);
            return new Runnable() {
                private String[] arr = inputArr;
                public void run() {
                    try {
                        for (int i = 0; i < arr.length; i=i+2) {
                            synchronized (threadToGo) {
                                while (threadToGo.value == 2)
                                    threadToGo.wait();
                                Helper.print(arr[i], arr[i + 1]);
                                threadToGo.value = 2;
                                threadToGo.notify();
                            }
                        }
                    } catch (InterruptedException e) {
                        System.out.println("Oops...");
                    }
                }
            };
        }
        public Runnable newThreadTwo() {
            final String[] inputArr = Helper.buildCharArr(26);
            return new Runnable() {
                private String[] arr = inputArr;
                public void run() {
                    try {
                        for (int i = 0; i < arr.length; i++) {
                            synchronized (threadToGo) {
                                while (threadToGo.value == 1)
                                    threadToGo.wait();
                                Helper.print(arr[i]);
                                threadToGo.value = 1;
                                threadToGo.notify();
                            }
                        }
                    } catch (InterruptedException e) {
                        System.out.println("Oops...");
                    }
                }
            };
        }
        class ThreadToGo {
            int value = 1;
        }
        public static void main(String args[]) throws InterruptedException {
            MethodOne one = new MethodOne();
            Helper.instance.run(one.newThreadOne());
            Helper.instance.run(one.newThreadTwo());
            Helper.instance.shutdown();
        }
    }
    
    • 利用volatile关键字
      volatile修饰的变量值直接存在main memory里面,子线程对该变量的读写直接写入main memory,而不是像其它变量一样在local thread里面产生一份copy。volatile能保证所修饰的变量对于多个线程可见性,即只要被修改,其它线程读到的一定是最新的值。
    public class MethodThree {
        private volatile ThreadToGo threadToGo = new ThreadToGo();
        class ThreadToGo {
            int value = 1;
        }
        public Runnable newThreadOne() {
            final String[] inputArr = Helper.buildNoArr(52);
            return new Runnable() {
                private String[] arr = inputArr;
                public void run() {
                    for (int i = 0; i < arr.length; i=i+2) {
                        while(threadToGo.value==2){}
                        Helper.print(arr[i], arr[i + 1]);
                        threadToGo.value=2;
                    }
                }
            };
        }
        public Runnable newThreadTwo() {
            final String[] inputArr = Helper.buildCharArr(26);
            return new Runnable() {
                private String[] arr = inputArr;
                public void run() {
                    for (int i = 0; i < arr.length; i++) {
                        while(threadToGo.value==1){}
                        Helper.print(arr[i]);
                        threadToGo.value=1;
                    }
                }
            };
        }
        public static void main(String args[]) throws InterruptedException {
            MethodThree three = new MethodThree();
            Helper.instance.run(three.newThreadOne());
            Helper.instance.run(three.newThreadTwo());
            Helper.instance.shutdown();
        }
    }
    

    6、线程死锁

    死锁

    相关文章

      网友评论

        本文标题:java进阶(二):多线程

        本文链接:https://www.haomeiwen.com/subject/gsiznftx.html