java基础之多线程

java基础之多线程

1. 多线程概述

1.1 线程与进程

  进程:进程指正在运行的程序。确切的来讲,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程当中的程序,而且每一个进程都具备必定独立功能。
  线程:线程是进程中的一个执行单元,来完成进程中的某个功能。负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是能够有多个线程的,这个应用程序也能够称之为多线程程序。
  简而言之:一个程序运行后至少有一个进程,一个进程中能够包含多个线程。多线程

1.2 程序运行原理

  • 分时调度
    全部线程轮流使用 CPU 的使用权,平均分配每一个线程占用 CPU 的时间。
  • 抢占式调度
    优先让优先级高的线程使用 CPU,若是线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度

2. 线程的建立和启动

2.1 Thread类

  继承Thread类建立线程并发

  • 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就表明了线程要完成的任务。所以把run()方法称为线程执行体。
  • 建立Thread子类的实例,即建立线程对象。
  • 调用线程对象的start()方法来启动线程。
public class ThreadTest extends Thread{

    private int i;
    public void run() {
        for(i=0;i<50;i++) {
            //当线程继承Thread类的时候,直接调用this便可获取当前的进程
            //Thread类的getName()方法会返回线程的名字
            System.out.println(getName()+" "+i);
        }
    }
    public static void main(String[] args) {
        for(int i =0;i<50;i++) {
            System.out.println(Thread.currentThread().getName()+" "+i);
            if(i==10) {
                //建立第一个线程
                new ThreadTest().start();
                //建立第二个线程
                new ThreadTest().start();
            }
        }
    }
}

2.2 Runnable接口

  实现Runnable接口建立线程类ide

  • 定义Runnable接口的实现类,并重写run()方法。
  • 建立Runnable实现类的实例,并以此实例做为Thread的target来建立Thread方法。
  • 调用线程的start()方法来启动对象。
public class RunnableTest implements Runnable {
    private int i;
    @Override
    public void run() {
        for(i=0;i<50;i++) {
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
    }

    public static void main(String[] args) {
        for(int i =0;i<50;i++) {
            System.out.println(Thread.currentThread().getName()+" "+i);
            if(i==10) {
                RunnableTest rs = new RunnableTest();
                new Thread(rs).start();
                new Thread(rs).start();
            }
        }
    }
}

  实现Runnable的原理和好处函数

  • 程序设计遵循的原则:开闭原则,对修改关闭,对扩展开放,减小线程自己和任务之间的耦合性。
  • 实现Runnable接口避免了单继承的局限性,因此较为经常使用。实现Runnable接口的方式,更加的符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。
  • 继承Thread类,线程对象和线程任务耦合在一块儿。一旦建立Thread类的子类对象,既是线程对象,又有线程任务。
  • 实现runnable接口,将线程任务单独分离出来封装成对象,类型就是Runnable接口类型。Runnable接口对线程对象和线程任务进行解耦。

2.3 匿名内部类

  使用线程的内匿名内部类方式,能够方便的实现每一个线程执行不一样的线程任务操做。this

  • 方式1:建立线程对象时,直接重写Thread类中的run方法
new Thread() {
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName() + " " + i);
        }
    }
}.start();
  • 方式2:使用匿名内部类的方式实现Runnable接口,从新Runnable接口中的run方法
new Thread(new Runnable() {
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }
}).start();

2.4 多线程经常使用方法

  • 获取名字
    经过getName()方法获取线程对象的名字
  • 设置名字
    • 经过构造函数能够传入String类型的名字
    new Thread("yyy") {
        public void run() {
            //do something...
        }
    }.start();
    • 经过setName(String)方法能够设置线程对象的名字
    new Thread() {
        public void run() {
            this.setName("abc");
            for(int i = 0; i < 100; i++) {
                System.out.println(this.getName() + "....bb");
            }
        }
    }.start();
  • 获取当前线程对象
    经过Thread.currentThread(), 主线程也能够获取
  • 休眠线程
    Thread.sleep(毫秒,纳秒), 控制当前线程休眠若干毫秒
  • 守护线程
    setDaemon(), 设置一个线程为守护线程, 该线程不会单独执行, 当其余非守护线程都执行结束后, 自动退出
Thread t1 = new Thread() {
    public void run() {
        for(int i = 0; i < 50; i++) {
            System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaaaa");
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
};
Thread t2 = new Thread() {
    public void run() {
        for(int i = 0; i < 2; i++) {
            System.out.println(getName() + "...bb");
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
};

t1.setDaemon(true);                     //将t1设置为守护线程

t1.start();
t2.start();
//运行结果
Thread-1...bb
Thread-0...aaaaaaaaaaaaaaaaaaaaaa
Thread-0...aaaaaaaaaaaaaaaaaaaaaa
Thread-1...bb
Thread-0...aaaaaaaaaaaaaaaaaaaaaa
  • 加入线程
    • join(), 当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续
    • join(int), 能够等待指定的毫秒以后继续
    final Thread t1 = new Thread() {
        public void run() {
            for(int i = 0; i < 100; i++) {
                System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaaaa");
    
            }
        }
    };
    Thread t2 = new Thread() {
        public void run() {
            for(int i = 0; i < 100; i++) {
                System.out.println(getName() + "...bb");
                try {
                    t1.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
            }
        }
    };
    t1.start();
    t2.start();

    2.5 同步代码块和同步方法

  • 同步代码块
      当多线程并发,有多段代码同时执行时,咱们但愿某一段代码执行的过程当中CPU不要切换到其余线程工做,这时就须要同步。使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步代码块。多个同步代码块若是使用相同的锁对象,那么他们就是同步的。若是两段代码是同步的, 那么同一时间只能执行一段, 在一段代码没执行结束以前, 不会执行另一段代码。
public class ThreadT {

    public void print1() {
        synchronized (ThreadT.class) {
            System.out.print("1");
            System.out.print("2");
            System.out.print("3");
            System.out.print("\r\n");
        }

    }

    public void print2() {
        synchronized (ThreadT.class) {
            System.out.print("a");
            System.out.print("b");
            System.out.print("c");
            System.out.print("\r\n");
        }

    }

    public static void main(String[] args) {
        ThreadT tT = new ThreadT();
        Thread t1 = new Thread() {
            public void run() {
                for (int i = 0; i < 100; i++) {
                    tT.print1();
                }
            }
        };
        Thread t2 = new Thread() {
            public void run() {
                for (int i = 0; i < 100; i++) {
                    tT.print2();
                }
            }
        };
        t1.start();
        t2.start();
    }
}
  • 同步方法
      使用synchronized关键字修饰一个方法, 该方法中全部的代码都是同步的。
public class ThreadT {

    public synchronized void print1() {
        System.out.print(Thread.currentThread().getName()+"----");
        System.out.print("1");
        System.out.print("2");
        System.out.print("3");
        System.out.print("\r\n");
    }

    public synchronized void print2() {
        System.out.print(Thread.currentThread().getName()+"----");
        System.out.print("a");
        System.out.print("b");
        System.out.print("c");
        System.out.print("\r\n");
    }

    public static void main(String[] args) {
        ThreadT tT = new ThreadT();
        Thread t1 = new Thread() {
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    tT.print1();
                }
            }
        };
        Thread t2 = new Thread() {
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    tT.print2();
                }
            }
        };
        t1.start();
        t2.start();
    }
}
//输出结果(一部分)
Thread-0----123
Thread-0----123
Thread-1----abc
Thread-1----abc
Thread-1----abc
Thread-0----123
Thread-0----123

2.6 线程间通讯

  多个线程并发执行时,在默认状况下CPU是随机切换线程的。若是咱们但愿他们有规律的执行,就可使用通讯,例如每一个线程执行一次打印。
  若是但愿线程等待,就调用wait(),若是但愿唤醒等待的线程,就调用notify();这两个方法必须在同步代码中执行,而且使用同步锁对象来调用。notifyAll()方法是唤醒全部线程,JDK5以前没法唤醒指定的一个线程,若是多个线程之间通讯,须要使用notifyAll()通知全部线程,用while来反复判断条件。线程

public class ThreadT {
    
    private int flag = 1;

    public synchronized void print1() {
        try {
            /*
            if (flag != 1) {    //if 语句是在那里等待就在那里起来
                this.wait();
            }*/
            while(flag != 1){
                this.wait();    //while循环是循环判断,每次都会判断标记
            }
            System.out.print("1");
            System.out.print("2");
            System.out.print("3");
            System.out.print("\r\n");
            flag = 2;
            //this.notify();
            this.notifyAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void print2() {
        try {
            while(flag != 2){
                this.wait();  
            }
            System.out.print("a");
            System.out.print("b");
            System.out.print("c");
            System.out.print("\r\n");
            flag = 3;
            this.notifyAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public synchronized void print3() {
        try {
            while(flag != 3){
                this.wait();  
            }
            System.out.print("+");
            System.out.print("-");
            System.out.print("*");
            System.out.print("\r\n");
            flag = 1;
            this.notifyAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        ThreadT tT = new ThreadT();
        Thread t1 = new Thread() {
            public void run() {
                for (int i = 0; i < 100; i++) {
                    tT.print1();
                }
            }
        };
        Thread t2 = new Thread() {
            public void run() {
                for (int i = 0; i < 100; i++) {
                    tT.print2();
                }
            }
        };
        Thread t3 = new Thread() {
            public void run() {
                for (int i = 0; i < 100; i++) {
                    tT.print3();
                }
            }
        };
        t1.start();
        t2.start();
        t3.start();
    }
}
//输出结果(一部分)
......
abc
+-*
123
abc
+-*
123
......

2.7 互斥锁

  使用ReentrantLock类的lock()和unlock()方法进行同步。使用Condition的await()和signal()来暂停和唤醒线程设计

public class ThreadT {

    private int flag = 1;
    private ReentrantLock rlock = new ReentrantLock();
    private Condition c1 = rlock.newCondition();
    private Condition c2 = rlock.newCondition();
    private Condition c3 = rlock.newCondition();

    public void print1() {
        rlock.lock();   //得到锁
        try {
            while (flag != 1) {
                c1.await();  //使当前线程等待,直到发出信号或中断
            }
            System.out.print("1");
            System.out.print("2");
            System.out.print("3");
            System.out.print("\r\n");
            flag = 2;
            c2.signal();    //唤醒等待线程。 
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            rlock.unlock();   //释放锁
        }
    }

    public void print2() {
        rlock.lock();
        try {
            while (flag != 2) {
                c2.await();
            }
            System.out.print("a");
            System.out.print("b");
            System.out.print("c");
            System.out.print("\r\n");
            flag = 3;
            c3.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            rlock.unlock();
        }
    }

    public void print3() {
        rlock.lock();
        try {
            while (flag != 3) {
                c3.await();
            }
            System.out.print("+");
            System.out.print("-");
            System.out.print("*");
            System.out.print("\r\n");
            flag = 1;
            c1.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            rlock.unlock();
        }
    }

    public static void main(String[] args) {
        ThreadT tT = new ThreadT();
        Thread t1 = new Thread() {
            public void run() {
                for (int i = 0; i < 100; i++) {
                    tT.print1();
                }
            }
        };
        Thread t2 = new Thread() {
            public void run() {
                for (int i = 0; i < 100; i++) {
                    tT.print2();
                }
            }
        };
        Thread t3 = new Thread() {
            public void run() {
                for (int i = 0; i < 100; i++) {
                    tT.print3();
                }
            }
        };
        t1.start();
        t2.start();
        t3.start();
    }
}
相关文章
相关标签/搜索