美文网首页
JAVA多线程入门

JAVA多线程入门

作者: onlyHalfSoul | 来源:发表于2018-05-25 14:32 被阅读85次

    继承Thread父类

    线程代码执行顺序和调用顺序无关,例如:

    public class MyThread extends Thread {
    
        @Override
        public void run(){
            super.run();
            System.out.println("MyThread");
        }
    /**运行顺序存疑
     * 并没有发现随机性 */
        public static void main(String[] args) {
            MyThread thread = new MyThread();
            thread.run();
            System.out.println("mainThread");
    
        }
        
    }
    

    上述代码执行理论上“MyThread”和“mainThread”打印顺序是随机的,和调用顺序无关,实际情况存疑。

    线程执行具有随机性,CPU的执行具有不确定性

    public class MyThread1 extends Thread {
    
        @Override
        public void run(){
            try {
                for (int i = 0;i < 10;i++){
                int time = (int) (Math.random()*1000);
                Thread.sleep(time);
                System.out.println("run:"+Thread.currentThread().getName());
                }
            }catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
        public static void main(String[] args) throws InterruptedException {
            MyThread1 thread = new MyThread1();
            thread.setName("myThread");
            thread.start();
            for (int i = 0; i<10;i++){
                int time = (int) (Math.random()*1000);
                    Thread.sleep(time);
                    System.out.println("main:"+Thread.currentThread().getName());
            }
    
        }
    }
    
    /*结果:
    run:myThread
    run:myThread
    run:myThread
    main:main
    main:main
    run:myThread
    run:myThread
    run:myThread
    main:main
    main:main
    main:main
    main:main
    run:myThread
    run:myThread
    main:main
    run:myThread
    run:myThread
    main:main
    main:main
    main:main
    */
    
    

    start方法并不代表线程启动,线程启动顺序由CPU执行顺序决定,无序性。

    public class MyThread2 extends Thread {
        private int i;
        public MyThread2(int i){
            super();
            this.i = i;
        }
        @Override
        public void run(){
            System.out.println("myThread:"+i);
        }
    
        public static void main(String[] args){
            MyThread2 t1 = new MyThread2(1);
            MyThread2 t2 = new MyThread2(2);
            MyThread2 t3 = new MyThread2(3);
            MyThread2 t4 = new MyThread2(4);
            MyThread2 t5 = new MyThread2(5);
            MyThread2 t6 = new MyThread2(6);
            MyThread2 t7 = new MyThread2(7);
            MyThread2 t8 = new MyThread2(8);
            MyThread2 t9 = new MyThread2(9);
            MyThread2 t10 = new MyThread2(10);
            MyThread2 t11 = new MyThread2(11);
            MyThread2 t12 = new MyThread2(12);
            MyThread2 t13 = new MyThread2(13);
            MyThread2 t14 = new MyThread2(14);
            MyThread2 t15 = new MyThread2(15);
            t1.start();
            t2.start();
            t3.start();
            t4.start();
            t5.start();
            t6.start();
            t7.start();
            t8.start();
            t9.start();
            t10.start();
            t11.start();
            t12.start();
            t13.start();
            t14.start();
            t15.start();
        }
    }
    
    /*结果:
    myThread:2
    myThread:1
    myThread:3
    myThread:4
    myThread:7
    myThread:8
    myThread:11
    myThread:12
    myThread:15
    myThread:13
    myThread:14
    myThread:5
    myThread:6
    myThread:9
    myThread:10
     
    */
    

    Runnable接口构造线程

    java是单基础,继承Thread类有局限性,所以更多的是使用Runnable接口去新建线程,Thread类有构造方法使用Runnable接口新建线程。

    public class RunableTest implements Runnable {
    
        @Override
        public void run() {
            System.out.println("Runable线程运行中:"+Thread.currentThread().getName());
        }
    
        public static void main(String[] arg){
            RunableTest runableTest = new RunableTest();
            Thread thread = new Thread(runableTest);
            thread.start();
            System.out.println("mainThread:"+Thread.currentThread().getName() );
        }
    }
    
    

    实例变量与线程安全

    实例变量不共享

    线程间变量不共享,数据不共享情况:

    public class ShareThread extends Thread{
        private int count = 5;
        public ShareThread(String name){
            super();
            this.setName(name);
        }
        @Override
        public void run(){
            super.run();
            while (count >0){
                count--;
                System.out.println("由"+Thread.currentThread().getName()+"计算,count="+count);
            }
        }
    
        public static void main(String[] args){
            ShareThread shareThread1 = new ShareThread("A");
            ShareThread shareThread2 = new ShareThread("B");
            ShareThread shareThread3 = new ShareThread("C");
            shareThread1.start();
            shareThread2.start();
            shareThread3.start();
        }
    }
    
    /*
    由A计算,count=4
    由B计算,count=4
    由B计算,count=3
    由B计算,count=2
    由B计算,count=1
    由B计算,count=0
    由A计算,count=3
    由A计算,count=2
    由A计算,count=1
    由A计算,count=0
    由C计算,count=4
    由C计算,count=3
    由C计算,count=2
    由C计算,count=1
    由C计算,count=0
    */
    
    

    线程数据共享

    共享数据情况就是多个线程可以访问同一个变量。

    public class ShareThread1 extends Thread {
        private int count  = 10;
    
        @Override
        synchronized public void run(){
            super.run();
            count--;
            //不要使用for语句,因为使用同步后线程就没有运行机会了
            //一直由线程进行减法运算
            System.out.println("由"+Thread.currentThread().getName()+"计算,count="+count);
        }
    
        public static void main(String[] args){
            ShareThread1 thread1 = new ShareThread1();
            Thread a = new Thread(thread1,"A");
            Thread b = new Thread(thread1,"B");
            Thread c = new Thread(thread1,"C");
            Thread d = new Thread(thread1,"D");
            Thread e = new Thread(thread1,"E");
            Thread f = new Thread(thread1,"F");
            a.start();
            b.start();
            c.start();
            d.start();
            e.start();
            f.start();
        }
    
    }
    
    
    /*结果:
    由A计算,count=9
    由D计算,count=8
    由E计算,count=7
    由F计算,count=6
    由C计算,count=5
    由B计算,count=4
    */
    

    synchronized关键字表示执行多个线程时以排队的方式进行处理。线程执行时会上锁,执行完毕后会解锁,线程调用run()方法前会请求线程锁,若已经上锁,则会不断请求线程锁。

    System.out.println()使用时可能会发生“非线程安全”问题,里面打印i--时,会先执行i--,然后打印结果,造成线程安全问题。

    public class ShareThread2 extends Thread {
        private int i = 5;
        @Override
        public void run(){
            System.out.println("i="+ (i--) +",threadName="+Thread.currentThread().getName());
        //i--在println之前执行,故可能发生非线程安全问题
        }
    
        public static void main(String[] args) {
            ShareThread2 run = new ShareThread2();
            Thread t1 = new Thread(run);
            Thread t2 = new Thread(run);
            Thread t3 = new Thread(run);
            Thread t4 = new Thread(run);
            Thread t5 = new Thread(run);
            t1.start();
            t2.start();
            t3.start();
            t4.start();
            t5.start();
        }
    }
    
    /*
    i=4,threadName=Thread-1
    i=5,threadName=Thread-3
    i=2,threadName=Thread-5
    i=5,threadName=Thread-4
    i=3,threadName=Thread-2
    
    */
    

    常用函数

    currentThread()方法

    currentThread返回代码段被哪个线程调用的信息。

    public class CountOpertrate extends Thread {
        public CountOpertrate(){
            System.out.println("CountOpertate-build-start");
            System.out.println("Thread.currentThread().getName() = "+Thread.currentThread().getName());
            System.out.println("this.getName()"+this.getName());
            System.out.println("CountOpertrate-build-end");
        }
    
        @Override
        public void run(){
            System.out.println("run-start");
            System.out.println("Thread.currentThread().getName() = "+Thread.currentThread().getName());
            System.out.println("this.getName()"+this.getName());
            System.out.println("run-end");
        }
    
    
        public static void main(String[] args) {
            CountOpertrate countOpertrate = new CountOpertrate();
            Thread t = new Thread(countOpertrate);
            t.setName("TEST");
            t.start();
        }
    
    /*result:
    CountOpertate-build-start
    Thread.currentThread().getName() = main
    this.getName()Thread-0
    CountOpertrate-build-end
    run-start
    Thread.currentThread().getName() = TEST
    this.getName()Thread-0
    run-end
    */
    

    上述代码显示,Count构建时时用的main线程,run是跑在TEST线程上。

    isAlive()方法

    isAlive方法是判断当前线程是处于活动状态。

    public class IsAliveTest extends Thread {
        @Override
        public void run() {
            System.out.println("run = "+this.isAlive());
        }
    
        public static void main(String[] args) throws InterruptedException {
            IsAliveTest i = new IsAliveTest();
            System.out.println("start =="+i.isAlive());
            i.start();
            Thread.sleep(1000);
            System.out.println("end =="+i.isAlive());
        }
    }
    
    /*result:
    start ==false
    run = true
    end ==false
    */
    

    若将线程对象以构造参数传递给Thread对象进行start,结果会有差异。

    public class IsAliveTest1 extends Thread {
    
        public IsAliveTest1(){
            System.out.println("IsAliveTest1-Start");
            System.out.println("Thread.currentThread().getName() = "+Thread.currentThread().getName());
            System.out.println("Thread.currentThread().isAlive() = "+Thread.currentThread().isAlive());
            System.out.println("this.getName() = "+this.getName());
            System.out.println("this.isAlive() = "+this.isAlive());
            System.out.println("IsAliveTest1-end");
        }
        @Override
        public void run(){
            System.out.println("run-Start");
            System.out.println("Thread.currentThread().getName() = "+Thread.currentThread().getName());
            System.out.println("Thread.currentThread().isAlive() = "+Thread.currentThread().isAlive());
            System.out.println("this.getName() = "+this.getName());
            System.out.println("this.isAlive() = "+this.isAlive());
            System.out.println("run-end");
        }
    
        public static void main(String[] args) throws InterruptedException {
            IsAliveTest1 test1 = new IsAliveTest1();
            Thread t1 = new Thread(test1);
            System.out.println("main bigin t1 isAlive = "+t1.isAlive());
            t1.setName("AAA");
            t1.start();
            Thread.sleep(1000);
            System.out.println("main end t1 isAlive = "+t1.isAlive());
        }
    }
    
    /*result:
    IsAliveTest1-Start
    Thread.currentThread().getName() = main
    Thread.currentThread().isAlive() = true
    this.getName() = Thread-0
    this.isAlive() = false
    IsAliveTest1-end
    main bigin t1 isAlive = false
    run-Start
    Thread.currentThread().getName() = AAA
    Thread.currentThread().isAlive() = true
    this.getName() = Thread-0
    this.isAlive() = false
    run-end
    main end t1 isAlive = false
    */
    
    

    sleep()方法

    sleep()方法是在括号中毫秒内使正在执行的线程暂停执行的方法,正在执行的线程是this.currentThread()返回的线程。

    public class sleepTest extends Thread {
        @Override
        public void run() {
            try {
                System.out.println("run threadName = "+this.getName()+"-begin");
                Thread.sleep(2000);
                System.out.println("run ThreadName = "+this.getName()+"-end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
            sleepTest test = new sleepTest();
            System.out.println("begin = "+ System.currentTimeMillis());
            test.run();
            //test.start();
            System.out.println("end = "+System.currentTimeMillis());
        }
    }
    
    /*直接用run()方法
    begin = 1521341785159
    run threadName = Thread-0-begin
    run ThreadName = Thread-0-end
    end = 1521341787160
    */
    /*使用start()方法
    begin = 1521341902632
    end = 1521341902632
    run threadName = Thread-0-begin
    run ThreadName = Thread-0-end
    
    main和sleepTest线程是异步的,所以先打印时间
    */
    

    停止线程

    interrupt()方法

    interrupt方法并不是立刻停止线程。而是在当前线程中打一个停止标记。

    public class InterruptTest extends Thread {
    
        @Override
        public void run() {
            super.run();
            for (int i = 0; i<50000;i++){
                System.out.println("i = "+ (i+1));
            }
        }
    
        public static void main(String[] args) {
            try {
                InterruptTest test = new InterruptTest();
                test.start();
                Thread.sleep(2000);
                Thread.interrupted();
            } catch (InterruptedException e) {
                System.out.println("main-catch");
                e.printStackTrace();
            }
        }
    }
    /*
    无法停止,打印50000条记录
    */
    

    判断线程是否是停止状态

    interrupted()方法,测试当前线程是否已经是中断状态,执行后将状态标志改为false。
    isInterrupted()方法,测试线程对象是否已经为中断状态,但不清除状态标志。

    异常法停止线程

    可以使用isInterrupted方法判断线程停止标志状态并抛出InterruptedException,使用interrupt()方法停止线程后,因为接收到停止状态码,抛出异常进入catch分支,继而终止线程。

    public class StopThreadTest extends Thread {
    
        @Override
        public void run() {
            super.run();
            try {
                for (int i=0;i<1000000;i++){
                    if (this.isInterrupted()){
                        System.out.println("已是停止状态,线程退出!");
                        throw new InterruptedException();
                    }
                    System.out.println("i = "+(i+1));
                }
                System.out.println("for下面的");
            } catch (InterruptedException e) {
                System.out.println("线程run()方法catch!线程异常终止");
                e.printStackTrace();
            }
    
        }
    
        public static void main(String[] args) {
    
            try {
                StopThreadTest test = new StopThreadTest();
                test.start();
                Thread.sleep(1000);
                test.interrupt();
            } catch (InterruptedException e) {
                System.out.println("main  catch");
                e.printStackTrace();
            }
            System.out.println("end!");
        }
    
    }
    
    /*result:
    ... ...
    i = 274280
    i = 274281
    i = 274282
    i = 274283
    i = 274284
    end!
    已是停止状态,线程退出!
    线程run()方法catch!线程异常终止
    java.lang.InterruptedException
        at com.tz.StopThread.StopThreadTest.run(StopThreadTest.java:15)
    
    */
    

    沉睡中停止进程

    线程在sleep状态下停止,会直接报异常,并进入catch退出,有两种情况,一个是先sleep再interrupt,还有就是先interrupt再停止。

    //先sleep
    
    public class StopSleep1 extends Thread{
    
        @Override
        public void run() {
            super.run();
            try {
                System.out.println("run-begin");
                Thread.sleep(200000);
                System.out.println("run-end");
            } catch (InterruptedException e) {
                System.out.println("在沉睡中停止,run()进入catch  "+this.isInterrupted());
                e.printStackTrace();
            }
        }
    
    
        public static void main(String[] args) {
            try {
                StopSleep1 sleep1 = new StopSleep1();
                sleep1.start();
                Thread.sleep(200);
                sleep1.interrupt();
            } catch (InterruptedException e) {
                System.out.println("main-catch");
                e.printStackTrace();
            }
            System.out.println("end!");
        }
    }
    
    /*result:
    run-begin
    end!
    在沉睡中停止,run()进入catch  false
    java.lang.InterruptedException: sleep interrupted
        at java.lang.Thread.sleep(Native Method)
        at com.tz.StopThread.StopSleep1.run(StopSleep1.java:13)
    */
    
    //后sleep
    
    public class StopSleep2 extends Thread {
    
        @Override
        public void run() {
            super.run();
            try {
                for (int i = 0;i<100000;i++){
                    System.out.println("i = "+(i+1));
                }
                System.out.println("run-begin");
                Thread.sleep(200000);
                System.out.println("run-end");
            } catch (InterruptedException e) {
                System.out.println("先停止再遇到sleep,run()进入catch  "+this.isInterrupted());
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
    
                StopSleep2 sleep2 = new StopSleep2();
                sleep2.start();
                sleep2.interrupt();
                System.out.println("end!");
        }
    
    }
    
    /*result:
    i = 99997
    i = 99998
    i = 99999
    i = 100000
    run-begin
    先停止再遇到sleep,run()进入catch  false
    java.lang.InterruptedException: sleep interrupted
        at java.lang.Thread.sleep(Native Method)
        at com.tz.StopThread.StopSleep2.run(StopSleep2.java:16)
    */
    

    暴力停止线程

    使用stop方法停止线程,这个方法很暴力。

    public class StopThread extends Thread {
    
        private int i = 0;
    
        @Override
        public void run() {
            try {
                while (true){
                    i++;
                    System.out.println("i=" +i);
                    Thread.sleep(1000);
                }
    
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
            try {
                StopThread thread = new StopThread();
                thread.start();
                Thread.sleep(8000);
                thread.stop();
                System.out.println("stop暴力停止");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    

    stop方法已经作废,尽量不使用!!!

    stop方法释放锁,会造成数据不一致的结果。

    public class StopThread1 extends Thread {
    
        private SynchronizedObject object;
    
        public StopThread1(SynchronizedObject object){
            super();
            this.object = object;
        }
    
        @Override
        public void run() {
            object.printString("b","bb");
        }
    
    
        public static void main(String[] args) {
    
            try {
                SynchronizedObject object = new SynchronizedObject();
                StopThread1 thread1 = new StopThread1(object);
                thread1.start();
                Thread.sleep(500);
                thread1.stop();
                System.out.println("object.getUsername()="+object.getUsername());
                System.out.println("object.getPassword()="+object.getPassword());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
        }
    }
    
    /*result:
    object.getUsername()=b
    object.getPassword()=aa
    */
    

    return 停止线程

    可以将interrupt()方法与return结合实现停止线程。

    public class ReturnStopThread extends Thread{
    
        @Override
        public void run() {
            while(true){
                if (this.isInterrupted()){
                    System.out.println("停止!");
                    return;
                }
                System.out.println("timer = "+System.currentTimeMillis());
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            ReturnStopThread thread = new ReturnStopThread();
            thread.start();
            Thread.sleep(2000);
            thread.interrupt();
        }
    }
    
    /*result:
    ... ...
    timer = 1521358261861
    timer = 1521358261861
    timer = 1521358261861
    timer = 1521358261861
    timer = 1521358261861
    timer = 1521358261861
    timer = 1521358261861
    停止!
    */
    

    建议还是使用抛异常来停止进程,因为抛异常可以通过catch语句将线程停止事件上抛,是线程停止事件得以传播。

    暂停线程

    暂停线程意味着次现场可以恢复运行,在Java多线程中可以使用suspend()方法暂停线程,使用resume()方法恢复线程的执行。

    public class SuspendTestThread extends Thread {
        private long i = 0;
        public long getI(){
            return i;
        }
    
        public void setI(long i) {
            this.i = i;
        }
    
        @Override
        public void run() {
            while(true){
                i++;
            }
        }
    
    
        public static void main(String[] args) {
            try {
                SuspendTestThread thread = new SuspendTestThread();
                thread.start();
                Thread.sleep(1000);
                //A段
                thread.suspend();
                System.out.println("线程暂停!");
                System.out.println("A= " +System.currentTimeMillis()+" i="+thread.getI());
                Thread.sleep(1000);
                System.out.println("A= " +System.currentTimeMillis()+" i="+thread.getI());
    
                //B段
                thread.resume();
                Thread.sleep(1000);
                System.out.println("线程唤醒!");
                System.out.println("B= " +System.currentTimeMillis()+" i="+thread.getI());
                Thread.sleep(1000);
                System.out.println("B= " +System.currentTimeMillis()+" i="+thread.getI());
    
                //c段
                thread.suspend();
                System.out.println("线程又暂停");
                System.out.println("C= " +System.currentTimeMillis()+" i="+thread.getI());
                Thread.sleep(1000);
                System.out.println("C= " +System.currentTimeMillis()+" i="+thread.getI());
    
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    /*result:
    线程暂停!
    A= 1521516350803 i=620113660
    A= 1521516351803 i=620113660
    线程唤醒!
    B= 1521516352804 i=1259895883
    B= 1521516353804 i=1901121177
    线程又暂停
    C= 1521516353804 i=1901176584
    C= 1521516354804 i=1901176584
    
    */
    

    明显线程在A和C段暂停执行了,在B段唤醒之后又能重新执行。

    suspend和rusume的缺点

    1. 独占

    使用线程暂停时,如果使用不当,容易造成对公共的同步对象的独占,导致其他线程无法访问公共同步对象。

    //model.class
    public class SynchronizedObject {
        synchronized public void printString(){
            System.out.println("begin");
            if (Thread.currentThread().getName().equals("a")){
                System.out.println("a线程永久陷入沉睡!");
                Thread.currentThread().suspend();
            }
            System.out.println("end");
        }
    }
    
    
    public class SuspendTestThread1 extends Thread {
        public static void main(String[] args) {
            try {
                final SynchronizedObject object = new SynchronizedObject();
                Thread thread1 = new Thread(){
                    @Override
                    public void run() {
                        object.printString();
                    }
                };
                thread1.setName("a");
                thread1.start();
                Thread.sleep(1000);
                Thread thread2 = new Thread(){
                    @Override
                    public void run() {
                        System.out.println("thraed2启动,但进入不了printString()方法");
                        System.out.println("因为printString()方法被a线程锁定并独占了");
                        object.printString();
                    }
                };
                thread2.start();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
     /*result:
     begin
    a线程永久陷入沉睡!
    thraed2启动,但进入不了printString()方法
    因为printString()方法被a线程锁定并独占了
     */
    
    
    1. 不同步
      因为线程暂停可能会导致数据不同步的情况。
    public class MyObject {
        private String username = "l";
        private String password = "ll";
        public void setValue(String username,String password){
            this.username = username;
            if (Thread.currentThread().getName().equals("a")){
                System.out.println("停止a线程!");
                Thread.currentThread().suspend();
            }
            this.password = password;
        }
        public void printUsernamePassword(){
            System.out.println(username+"  "+password);
        }
    }
    
    
    public class SuspendTestThread2 extends Thread {
        public static void main(String[] args) throws InterruptedException {
            final MyObject myObject = new MyObject();
            Thread thread1 = new Thread(){
                @Override
                public void run() {
                    myObject.setValue("a","aa");
                }
            };
            thread1.setName("a");
            thread1.start();
            Thread.sleep(500);
            Thread thread2 = new Thread(){
                @Override
                public void run(){
                    myObject.printUsernamePassword();
                }
            };
            thread2.start();
        }
    }
    
    
    /*result: 
    停止a线程!
    a  ll
    */
    

    suspend()和resume()方法已经废弃,不建议使用,可以研究。

    yield()方法

    yield()方法是让当前线程放弃cpu资源,但放弃的时间不确定,可能刚刚放弃就立刻获得cpu资源。

    public class YieldTestThread extends Thread {
        @Override
        public void run() {
            long beginTime = System.currentTimeMillis();
            int count = 0;
            for (int i = 0;i < 50000000; i++){
                //Thread.yield();
                count = count + (i+1);
            }
            long endTime = System.currentTimeMillis();
            System.out.println("用时:"+(endTime-beginTime)+"毫秒");
        }
    
    
        public static void main(String[] args) {
            YieldTestThread thread = new YieldTestThread();
            thread.start();
        }
    }
    
    
    /*result1:(不加yield)
    用时:18毫秒
    */
    
    /*result2:(加yield)
    用时:3362毫秒
    */
    

    线程优先级

    线程可以划分优先级,从1-10级,其他会报错。
    线程的优先级具有承继性。
    优先级规则,总是大部分先执行优先级高的线程。

    //线程1
    public class PriorityTestThread extends Thread{
        @Override
        public void run() {
            long beginTime = System.currentTimeMillis();
            long addResult = 0;
            for (int j = 0;j < 10;j++){
                for(int i = 0;i<50000;i++){
                    Random random = new Random();
                    random.nextInt();
                    addResult = addResult+1;
                }
            }
            long endTime = System.currentTimeMillis();
            System.out.println("* * * * * * thread 1 use time="+(endTime - beginTime));
        }
    }
    
    //线程2
    public class PriorityTestThread1 extends Thread {
    
        @Override
        public void run() {
            long beginTime = System.currentTimeMillis();
            long addResult = 0;
            for (int j = 0;j < 10;j++){
                for(int i = 0;i<50000;i++){
                    Random random = new Random();
                    random.nextInt();
                    addResult = addResult+1;
                }
            }
            long endTime = System.currentTimeMillis();
            System.out.println("* * * * * * thread 2 use time="+(endTime - beginTime));
        }
    }
    
    public class Run {
        public static void main(String[] args) {
            for (int i = 0;i < 100;i++){
                PriorityTestThread thread1 = new PriorityTestThread();
                thread1.setPriority(10);
                thread1.start();
                PriorityTestThread1 thread2 = new PriorityTestThread1();
                thread2.setPriority(1);
                thread2.start();
            }
        }
    
    }
    
    /*result:
    ... ...
    * * * * * * thread 1 use time=6197
    * * * * * * thread 1 use time=6207
    * * * * * * thread 1 use time=6252
    * * * * * * thread 1 use time=6270
    * * * * * * thread 2 use time=6870
    * * * * * * thread 2 use time=6524
    * * * * * * thread 2 use time=7036
    * * * * * * thread 1 use time=7522
    * * * * * * thread 1 use time=6448
    * * * * * * thread 2 use time=7035
    * * * * * * thread 2 use time=7223
    * * * * * * thread 2 use time=7025
    * * * * * * thread 1 use time=7776
    * * * * * * thread 1 use time=6747
    * * * * * * thread 2 use time=7261
    * * * * * * thread 1 use time=7939
    ... ...
    */
    

    优先级高的不是一定先执行。

    守护线程

    守护线程是一种特殊线程,特性有“陪伴”的含义,当进程中不存在非守护进程时,守护进程就自动销毁了。典型的守护进程就是垃圾回收线程(垃圾回收器 GC)

    public class DaemonTestThread extends Thread {
        private int i = 0;
    
        @Override
        public void run() {
            try {
                while (true){
                    i++;
                    System.out.println("i = "+ i);
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
            try {
                DaemonTestThread thread = new DaemonTestThread();
                thread.setDaemon(true);
                thread.start();
                Thread.sleep(5000);
                System.out.println("我离开Thread对象也不再打印了,也就是停止了!");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
    }
    //线程thread为主线程的守护进程,主线程停止守护进程也结束。
    
    /*result:
    i = 1
    i = 2
    i = 3
    i = 4
    i = 5
    我离开Thread对象也不再打印了,也就是停止了!
    */
    

    对象及变量的并发访问

    synchronzed同步方法

    "非线程安全"会在多个线程对同一个对象中的实例变量进行并发访问时发生产生"脏读",也就是取到的数据其实是被更改过的。而线程安全就是以获得的实例变量的值是经过同步处理的,不会出现脏读现象。

    方法内数据为线程安全

    "非线程安全"问题存在于"实例变量"中,如果是方法内部私有变量则不存在"非线程安全问题"。

    public class HasSelfPrivateNum {
        public void addI(String username){
            try {
                int num = 0;
                if (username.equals("a")){
                    num = 100;
                    System.out.println("a set over!");
                    Thread.sleep(2000);
                }else {
                    num = 200;
                    System.out.println("b set over!");
                    Thread.sleep(2000);
                }
                System.out.println(username + " num = "+num);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    
    public class ThreadA extends Thread {
        private HasSelfPrivateNum numRef;
        public ThreadA(HasSelfPrivateNum numRef){
            super();
            this.numRef = numRef;
        }
    
        @Override
        public void run() {
            super.run();
            numRef.addI("a");
        }
    }
    
    
    
    public class ThreadB extends Thread {
        private HasSelfPrivateNum numRef;
        public ThreadB(HasSelfPrivateNum numRef){
            super();
            this.numRef = numRef;
        }
    
        @Override
        public void run() {
            super.run();
            numRef.addI("b");
        }
    
    }
    
    
    
    public class Run {
        public static void main(String[] args) {
            HasSelfPrivateNum numRef = new HasSelfPrivateNum();
            ThreadA threadA = new ThreadA(numRef);
            threadA.start();
            ThreadB threadB = new ThreadB(numRef);
            threadB.start();
        }
    }
    
    
    
    /*result:
    a set over!
    b set over!
    a num = 100
    b num = 200
    
    */
    

    实例变量非线程安全

    若多个线程访问一个对象实例中的实例变量。则可能发生“非线程安全”问题。
    用线程访问的对象中如果有多个实例变量,则运行的结果有可能出现交叉的情况。
    如果对象仅有一个实例变量,则有可能出现覆盖的情况。

    public class HasSelfPrivateNum{
        private int num = 0;
        
        //addI()方法前加上synchronized关键字,避免“非线程安全问题”
       synchronized public void addI(String username){
            try {
                if (username.equals("a")){
                    num = 100;
                    System.out.println("a set over!");
                    Thread.sleep(2000);
                }else {
                    num = 200;
                    System.out.println("b set over!");
                    Thread.sleep(2000);
                }
                System.out.println(username + " num = "+num);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    
    public class ThreadA extends Thread {
        private HasSelfPrivateNum numRef;
        public ThreadA(HasSelfPrivateNum numRf){
            super();
            this.numRef = numRf;
        }
    
        @Override
        public void run() {
            super.run();
            numRef.addI("a");
        }
    }
    
    
    public class ThreadB extends Thread {
        private HasSelfPrivateNum numRef;
        public ThreadB(HasSelfPrivateNum numRf){
            super();
            this.numRef = numRf;
        }
    
        @Override
        public void run() {
            super.run();
            numRef.addI("b");
        }
    
    }
    
    
    public class Run {
        public static void main(String[] args) {
            HasSelfPrivateNum numRef = new HasSelfPrivateNum();
            ThreadA threadA = new ThreadA(numRef);
            threadA.start();
            ThreadB threadB = new ThreadB(numRef);
            threadB.start();
        }
    }
    
    
    /*不加synchronized关键字:
    a set over!
    b set over!
    b num = 200
    a num = 200
    */
    
    
    /*加synchronized关键字:
    a set over!
    a num = 100
    b set over!
    b num = 200
    */
    

    多个对象多个锁

    synchronized关键字取得的锁都对象锁,哪个线程先执行带有synchronized关键字的方法就先获得对象锁,其他线程只能依次等待执行完成。

    public class LockTestObject {
       synchronized public void methodA(){
           try {
               System.out.println("Begin methodA threadName = "+Thread.currentThread().getName());
               Thread.sleep(5000);
               System.out.println("methodA end! endTime = "+System.currentTimeMillis());
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
    
      synchronized public void methodB(){
           try {
               System.out.println("Begin methodB threadName = "+Thread.currentThread().getName());
               Thread.sleep(5000);
               System.out.println("methodB end! endTime = "+System.currentTimeMillis());
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
    }
    
    
    
    public class LockThreadA extends Thread {
       private LockTestObject object;
       public LockThreadA(LockTestObject object){
           super();
           this.object = object;
       }
    
       @Override
       public void run() {
           super.run();
           object.methodA();
       }
    }
    
    
    
    public class LockThreadB extends Thread {
       private LockTestObject object;
       public LockThreadB(LockTestObject object){
           super();
           this.object = object;
       }
    
       @Override
       public void run() {
           super.run();
           object.methodB();
       }
    }
    
    
    public class Run {
       public static void main(String[] args) {
           LockTestObject object = new LockTestObject();
           LockThreadA threadA = new LockThreadA(object);
           threadA.setName("A");
           LockThreadB threadB = new LockThreadB(object);
           threadB.setName("B");
           threadA.start();
           threadB.start();
       }
    }
    
    
    /*methodA方法不加synchronized关键字
    Begin methodB threadName = B
    Begin methodA threadName = A
    methodB end! endTime = 1521719664578
    methodA end! endTime = 1521719664579
    */
    
    /*methodA方法加上synchronized关键字
    Begin methodA threadName = A
    methodA end! endTime = 1521719556410
    Begin methodB threadName = B
    methodB end! endTime = 1521719561411
    */
    
    

    脏读

    所谓脏读是在对去实例变量时该变量已被其他线程改过,读出数据有误。

    public class PublicVar {
        public String userName = "A";
        public String password = "AA";
        synchronized public void setValue(String userName,String password){
            try {
                this.userName = userName;
                Thread.sleep(5000);
                this.password = password;
                System.out.println("setValue method thread name = "+Thread.currentThread().getName()+" userName = "+userName+" password = "+password);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        public void getValue(){
            System.out.println("setValue method thread name = "+Thread.currentThread().getName()+" userName = "+userName+" password = "+password);
        }
    }
    
    
    public class DirtyReadTestThread extends Thread{
        private PublicVar publicVar;
        public DirtyReadTestThread(PublicVar publicVar){
            super();
            this.publicVar = publicVar;
        }
    
        @Override
        public void run() {
            super.run();
            publicVar.setValue("B","BB");
        }
    }
    
    
    public class Run {
        public static void main(String[] args) {
            try {
                PublicVar publicVar = new PublicVar();
                DirtyReadTestThread thread = new DirtyReadTestThread(publicVar);
                thread.start();
                Thread.sleep(200);
                publicVar.getValue();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    
    /*result:
    setValue method thread name = main userName = B password = AA
    setValue method thread name = Thread-0 userName = B password = BB
    */
    

    如上所示,main线程出现了脏读,因为getValue()方法不是同步的,只需在getValue前加上synchronized关键字,即可保持数据同步性。

     synchronized public void getValue(){
            System.out.println("setValue method thread name = "+Thread.currentThread().getName()+" userName = "+userName+" password = "+password);
        }
        
    /*result:
    setValue method thread name = Thread-0 userName = B password = BB
    setValue method thread name = main userName = B password = BB
    */
    

    当线程调用对象包含的synchronized方法时获取了对象的X锁,但别的线程可以调用该实体非synchronized方法。

    synchronized 锁重入

    一个线程多次请求synchronized方法锁时,可以重复获得方法所在的对象实体的X锁
    锁重入,即可重复获得内部锁。

    public class Service {
        synchronized public void service1(){
            System.out.println("service1");
            service2();
        }
    
        synchronized public void service2(){
            System.out.println("service2");
            service3();
        }
        synchronized public void service3(){
            System.out.println("service3");
        }
    }
    
    public class LockReentryTestThread  extends Thread {
        @Override
        public void run() {
            Service service = new Service();
            service.service1();
        }
    }
    
    
    public class Run {
        public static void main(String[] args) {
            LockReentryTestThread thread = new LockReentryTestThread();
            thread.start();
        }
    }
    
    /*result:
    service1
    service2
    service3
    */
    

    service类中锁就重入了,三个service方法相互调用。

    锁重入也支持在父子类间的锁重用。

    出现异常,锁自动释放,其他线程继续调用。

    synchronized方法的弊端

    导致进程等待时间较长,失去多线程的意义,导致程序响应时间过长。
    synchronized同步块可以解决这个问题。

    synchronized同步代码块

    当两个并发的线程访问同一个对象object中的synchronized(this)同步块时,一段时间内只有一个线程能访问并执行,另一个线程必须等待前一个线程执行完毕这个代码块之后,才能执行这块代码。

    使用同步synchronized 代码块时,同一个object的同步代码块使用同一个对象监视器,执行一个同步块时对象中其他同步块会被阻塞。

    将任意对象作为对象监视器

    锁非this对象的优点是:若在一个类中有很多个synchronized方法,这时虽然能实现同步,但会收到阻塞,影响运行效率;若使用同步代码块锁非this对象,则synchronized(非this)代码块中的程序与同步方法是异步的,不与其他锁this的同步方法争抢this锁。则可提高运行效率。

    静态同步synchronized方法与synchronized(class)代码块。

    关键字还可以作用在static静态方法上,是对方法所在的类.class持锁,而不是对一个对象上锁。
    synchronized代码块也可以对class类上锁,实现同步。synchronized(xxx.class)。

    数据类型String的常量池特性

    由于JVM中String数据类型的常量池特性 a==b 返回true,所以不使用String对象作为对象监视器(对象锁)。

    同步synchronized方法无限等待与解决

    synchronized同步方法容易造成死循环,是形成陷入死锁。同步块可以解开这个死锁问题,死锁线程依旧跳不出,但其他线程可获得锁。

    多线程的死锁

    synchronized嵌套代码块将带来死锁。

    进入Cmd 输入 jsp 查找Run的id值 在输入 jstack -l 19560 可查看程序运行死锁情况

    内置类与静态内置类

    锁对象的改变

    public class MyService {
        private String lock = "123";
        public void testMethod() {
            try {
                synchronized (lock) {
                    System.out.println(Thread.currentThread().getName() + "  begin  " + System.currentTimeMillis());
                    lock = "456";
                    Thread.sleep(2000);
                    System.out.println(Thread.currentThread().getName() + "  end  " + System.currentTimeMillis());
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    
    public class ThreadA extends Thread {
        private MyService service;
        public ThreadA(MyService service) {
            super();
            this.service = service;
        }
    
        @Override
        public void run() {
            service.testMethod();
        }
    }
    
    
    public class ThreadB extends Thread{
        private MyService service;
        public ThreadB(MyService service) {
            super();
            this.service = service;
        }
    
        @Override
        public void run() {
            service.testMethod();
        }
    }
    
    
    public class Run1 {
        public static void main(String[] args) throws InterruptedException {
            MyService service = new MyService();
            ThreadA threadA = new ThreadA(service);
            threadA.setName("A");
    
            ThreadB threadB = new ThreadB(service);
            threadB.setName("B");
            threadA.start();
            //Thread.sleep(50 );
            threadB.start();
        }
    }
    
    
    /*延迟50毫秒,争抢两个锁
    A  begin  1523325785537
    B  begin  1523325785585
    A  end  1523325787539
    B  end  1523325787585
    */
    
    /*不延迟,争抢一个锁
    A  begin  1523325815403
    A  end  1523325817403
    B  begin  1523325817403
    B  end  1523325819404
    */
    

    只要锁对象不变,即使对象属性改变依旧同步,线程还是争抢一个锁。

    相关文章

      网友评论

          本文标题:JAVA多线程入门

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