java 多线程

299 阅读10分钟

两种开始多线程方式

Runable接口

public class RunnableStyle implements Runnable {

    public static void main(String[] args) {
        Thread thread = new Thread(new RunnableStyle());
        thread.start();
    }

    @Override
    public void run() {
        System.out.println("hello");
    }
}

继承Thread并重写run方法

public class ThreadStyle extends Thread {
    @Override
    public void run() {
        System.out.println("hello");
    }

    public static void main(String[] args) {

        new ThreadStyle().start();
    }
}

停止多线程

停止非阻塞的线程

一般情况下,线程退出可以使用while循环判断共享变量条件的方式,当线程内有阻塞操作时,可能导致线程无法运行到条件判断的地方而导致一直阻塞下去,这个时候就需要中断来帮助线程脱离阻塞。因此比较优雅的退出线程方式是结合共享变量和中断。

public class RightWayStop implements Runnable{

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new RightWayStop());
        thread.start();
        thread.sleep(100);
        thread.interrupt();

    }

    @Override
    public void run() {
        int i =0;
        while (!Thread.currentThread().interrupted()&&i<Integer.MAX_VALUE/2){
            if(i%10000==0){
                System.out.println(i);
            }
            i++;
            if(i>=Integer.MAX_VALUE/2){
                System.out.println("finished");
            }

        }

    }
}


停止阻塞的线程

对于处于sleep,join等操作的线程,如果被调用interrupt()后,会抛出InterruptedException,然后线程的中断标志位会由true重置为false,因为线程为了处理异常已经重新处于就绪状态。

public class RightWayStop {

    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = () -> {
            int i = 0;
            try {

                Thread.sleep(200000);
            } catch (InterruptedException e) {
                System.out.println(e);
                // e.printStackTrace();
            }
        };

        Thread thread = new Thread(runnable);
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }

}

Thead.sleep

Thread.sleep一旦执行 会清除interupted状态

中断线程

处理中断的两种姿势

注意:

  1. 不要屏蔽中断
  2. volatile的boolean无法处理长时间阻塞的情况

优先选择在方法上抛出异常

用throws InterruptedException 标记你的方法,不采用try 语句块捕获异常,以便于该异常可以传递到顶层,让run方法可以捕获这一异常.

恢复中断

如果不想或无法传递InterruptedException(例如用run方法的时候,就不让该方法throws InterruptedException),那么应该选择在catch 子句中调用Thread.currentThread().interrupt() 来恢复设置中断状态,以便于在后续的执行依然能够检查到刚才发生了中断。

public class RightWayStop implements Runnable{

    public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(new RightWayStop());
        thread.start();
        thread.sleep(3000);
        thread.interrupt();

    }

    private void reInterupt(){
        try {
            System.out.println("aaa");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            e.printStackTrace();
        }
    }


    @Override
    public void run() {

        while (true) {
            if(Thread.currentThread().isInterrupted()){
                System.out.println("break");
                break;
            }
            reInterupt();
        }
    }
}

Thread.interrupted() 和 thread.isInterrupted()

Thread.interrupted()返回当前线程是否被中断 并清除interrupt状态;

Thread对象.isInterrupted(); 返回该线程是否被中断;

synchronized

同一个时间只能执行一段代码块

抛出异常会直接释放锁

对象锁

this作为锁

public class RightWayStop {

    public static void main(String[] args) throws InterruptedException {

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                synchronized (this){
                    System.out.println("我是"+Thread.currentThread().getName());
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("结束:"+Thread.currentThread().getName());
                }
            }
        };

        Thread thread = new Thread(runnable);
        Thread thread1 = new Thread(runnable);
        thread.start();
        thread1.start();

        while (thread.isAlive()||thread1.isAlive()){

        }
        System.out.println("done");


    }




}

对象作为锁

相同的锁对象(lock0或者lock1)才会synchronized运行

public class RightWayStop {

    public static void main(String[] args) throws InterruptedException {
        Object lock0 = new Object();
        Object lock1 = new Object();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                synchronized (lock0) {
                    System.out.println("(lock)我是:" + Thread.currentThread().getName());
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("(lock)结束:" + Thread.currentThread().getName());
                }

                synchronized (lock1) {
                    System.out.println("(lock1)我是:" + Thread.currentThread().getName());
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("(lock1)结束:" + Thread.currentThread().getName());
                }
            }
        };

        Thread thread = new Thread(runnable);
        Thread thread1 = new Thread(runnable);
        thread.start();
        thread1.start();

        while (thread.isAlive() || thread1.isAlive()) {

        }
        System.out.println("done");


    }


}

方法锁(非static)

synchronized 的方法锁对象默认为this

public class RightWayStop {

    public synchronized void runMe() throws InterruptedException {

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

        Thread.sleep(1000);

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


    }

    public static void main(String[] args) throws InterruptedException {
        Object lock0 = new Object();
        Object lock1 = new Object();
        RightWayStop rightWayStop = new RightWayStop();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    rightWayStop.runMe();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        Thread thread = new Thread(runnable);
        Thread thread1 = new Thread(runnable);
        thread.start();
        thread1.start();

        while (thread.isAlive() || thread1.isAlive()) {

        }
        System.out.println("done");


    }


}

类锁

静态方法锁

synchronized静态方法上的所有Runnable的实例共用一把锁,普通的方法Runnable的实例不能锁

public class ClassLockThread implements Runnable{
    private static ClassLockThread instance1 = new ClassLockThread();
    private static ClassLockThread instance2 = new ClassLockThread();
    public static void main(String[] args) {
        Thread thread1 = new Thread(instance1);
        Thread thread2 = new Thread(instance2);
        thread1.start();
        thread2.start();
    }
    @Override
    public void run() {
        try {
            method();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private static synchronized void method() throws InterruptedException {
        System.out.println("开始:"+Thread.currentThread().getName());
        Thread.sleep(1000);
        System.out.println("结束:"+Thread.currentThread().getName());
    }
}

类锁代码块

public class ClassLockThread implements Runnable {
    private static ClassLockThread instance1 = new ClassLockThread();
    private static ClassLockThread instance2 = new ClassLockThread();

    public static void main(String[] args) {
        Thread thread1 = new Thread(instance1);
        Thread thread2 = new Thread(instance2);
        thread1.start();
        thread2.start();
    }

    @Override
    public void run() {
        try {
            method();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void method() throws InterruptedException {
        synchronized (ClassLockThread.class) {
            System.out.println("开始:" + Thread.currentThread().getName());
            Thread.sleep(1000);
            System.out.println("结束:" + Thread.currentThread().getName());
        }

    }
}

线程的状态

wait与notify

public class RightWayStop {
    public static Object object = new Object();

    static class Thread1 extends Thread {
        @Override
        public void run() {
            synchronized (object) {
                System.out.println("thread1 start");
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("thread1 finished");
            }
        }
    }

    static class Thread2 extends Thread {
        @Override
        public void run() {
            synchronized (object) {
                System.out.println("thread2 start");

                object.notify();

                System.out.println("thread2 finished");
            }
        }
    }


    public static void main(String[] args) throws InterruptedException {
        Thread1 thread1 = new Thread1();
        Thread2 thread2 = new Thread2();
        thread1.start();
        Thread.sleep(100);
        thread2.start();

    }

}

Sleep

sleep方法可以让线程进入Timed_Waiting状态,并且不占用CPU资源,但是不释放锁,直到规定时间后再执行,休眠期间如果被中断,会抛出异常并清除中断状态。

Sleep的替代方法

TimeUnit.SECONDS.sleep(1000);

Join

加入当前线程,并等待其运行完毕,再运行当前线程

/**
 * 描述:     演示join,注意语句输出顺序,会变化。
 */
public class RightWayStop {
    public static final Object object = new Object();
    public static int i=0;

    static class Thread1 extends Thread {
        @Override
        public void run() {

            try {
                Thread1.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"执行完毕");
        }
    }

    static class Thread2 extends Thread {
        @Override
        public void run() {
            try {
                Thread1.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"执行完毕");
        }
    }


    public static void main(String[] args) throws InterruptedException {

        Thread1 thread1 = new Thread1();
        Thread2 thread2 = new Thread2();
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println(Thread.currentThread().getName()+"执行完毕");

    }

}

Join的中断写法

/**
 * 描述:     演示join期间被中断的效果
 */
public class JoinInterrupt {
    public static void main(String[] args) {
        Thread mainThread = Thread.currentThread();
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    mainThread.interrupt();
                    Thread.sleep(5000);
                    System.out.println("Thread1 finished.");
                } catch (InterruptedException e) {
                    System.out.println("子线程中断");
                }
            }
        });
        thread1.start();
        System.out.println("等待子线程运行完毕");
        try {
            thread1.join();
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName()+"主线程中断了");
            thread1.interrupt();
        }
        System.out.println("子线程已运行完毕");
    }

}

Join等价写法

Thread退出时会调用notifyAll

synchronized (thread1){
        thread1.wait();
}

优先级

优先级不可靠,程序设计不应该依赖优先级

线程异常处理

1.在run方法里捕获异常 2.实现UncaughtExceptionHandler全局处理异常

public class ExceptionInChildThread implements Runnable {


    public static void main(String[] args) {
        Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler("catcher 1"));
        new Thread(new ExceptionInChildThread()).start();
        for (int i =0;i<1000;i++){
            System.out.println(i);
        }
    }
    @Override
    public void run() {
        throw new RuntimeException();
    }
}
public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
    private String name;

    public MyUncaughtExceptionHandler(String name) {
        this.name = name;
    }

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        Logger anonymousLogger = Logger.getAnonymousLogger();
        anonymousLogger.log(Level.WARNING,"线程异常"+t.getName(),e);
        System.out.println(name+"补货了异常");
    }
}

线程安全

《Java Concurrency In Practice》的作者Brian Goetz对“线程安全”有一个比较恰当的定义:“当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象是线程安全的”。

并发底层原理

jvm内存结构

java内存模型

重排序

java对象模型

重排序

编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序,在不改变程序语义的前提下,尽可能减少寄存器的读取、存储次数,充分复用寄存器的存储值。

/**
 * 描述:     演示重排序的现象 “直到达到某个条件才停止”,测试小概率事件
 */
public class OutOfOrderExecution {

    private static int x = 0, y = 0;
    private static int a = 0, b = 0;

    public static void main(String[] args) throws InterruptedException {
        int i = 0;
        for (; ; ) {
            i++;
            x = 0;
            y = 0;
            a = 0;
            b = 0;

            CountDownLatch latch = new CountDownLatch(3);

            Thread one = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        latch.countDown();
                        latch.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    a = 1;
                    x = b;
                }
            });
            Thread two = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        latch.countDown();
                        latch.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    b = 1;
                    y = a;
                }
            });
            two.start();
            one.start();
            latch.countDown();
            one.join();
            two.join();

            String result = "第" + i + "次(" + x + "," + y + ")";
            if (x == 0 && y == 0) {
                System.out.println(result);
                break;
            } else {
                System.out.println(result);
            }
        }
    }


}

可见性

一个线程对共享变量值的修改,能够及时地被其他线程看到需要时间。

public class FieldVisibility {
    int a = 1;
    int b = 2;


    public static void main(String[] args) {
        FieldVisibility test = new FieldVisibility();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                test.change();
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                test.print();
            }
        }).start();
    }

    private void print() {
        System.out.println("b:"+b);
        System.out.println("a:"+a);
    }

    private void change() {
        a = 3;
        b = a;
    }
}

发生可见性问题时 a=1,b=3 原因是虽然ab两个值已经被修改但是a的修改尚未被第二个线程看到 使用volitile可以修复这种问题

可见性原因

所有的共享变量存在于主内存中,每个线程有自己的本地内存,而且线程读写共享数据也是通过本地内存交换的,所以才导致了可见性问题。

happens-before

如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。

程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作; 锁定规则:一个unLock操作先行发生于后面对同一个锁额lock操作; volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作; 传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C; 线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作; 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生; 线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行; 对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始;

volatile的作用

1.可见性 2.禁止重排序 但是volatile无法保证原子性

用保证synchronized 可见性

public class FieldVisibilityABCD {

    int a = 1;
    int b = 2;
    int c = 2;
    int d = 2;

    private void change() {
        a = 3;
        b = 4;
        c = 5;
        synchronized (this) {
            d = 6;
        }
    }


    private void print() {
        synchronized (this) {
            int aa = a;
        }
        int bb = b;
        int cc = c;
        int dd = d;

        System.out.println("b=" + b + ";a=" + a);
    }

    public static void main(String[] args) {
        while (true) {
            FieldVisibilityABCD test = new FieldVisibilityABCD();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    test.change();
                }
            }).start();

            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    test.print();
                }
            }).start();
        }

    }


}

原子性

原子性:一个操作不能被打断,要么全部执行完毕,要么不执行。

默认的原子操作

1.除了long和double之外的基本类型的赋值操作(因为long和double类型是64位的,所以它们的操作在32位机器上不算原子操作,而在64位的机器上是原子操作。)(实际上,商用虚拟机不大可能出现)

2.引用

3.java.util.concurrent.atomic.*

双重检查单例模式

synchronized并不防止synchronized内部的重排序,并且synchronized可以和外部的if(instance==null){同时运行所以需要 volatile

/**
 * 线程 安全的双重检查单例
 */
public class Singleton6 {

    private volatile static Singleton6 instance;

    private Singleton6() {

    }

    public static Singleton6 getInstance() {
        if (instance == null) {
            synchronized (Singleton6.class) {
                if (instance == null) {
                    instance = new Singleton6();
                }
            }
        }
        return instance;
    }
}
/**
 * 线程 安全的静态内部类单例
 */
public class Singleton7 {

    private Singleton7() {
    }

    private static class SingletonInstance {

        private static final Singleton7 INSTANCE = new Singleton7();
    }

    public static Singleton7 getInstance() {
        return SingletonInstance.INSTANCE;
    }
}
/**
*枚举单例模式(线程安全)
*/
public enum Singleton8 {
    INSTANCE;

    public void whatever() {

    }
}

发现死锁

命令行

jps查找进程id

jstack 进程id

threadMXBean

public class DeadLock {
    static Object Lock1 = new Object();
    static Object Lock2 = new Object();

    static class Thread1 extends Thread{
        @Override
        public void run() {
           synchronized (Lock1){
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               synchronized (Lock2){

               }
           }
        }
    }

    static class Thread2 extends Thread{
        @Override
        public void run() {
            synchronized (Lock2){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (Lock1){

                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread1 thread1 = new Thread1();
        Thread2 thread2 = new Thread2();
        thread1.start();
        thread2.start();
        Thread.sleep(1000);

        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();
        for (int i = 0; i < deadlockedThreads.length ; i++) {
            ThreadInfo threadInfo = threadMXBean.getThreadInfo(deadlockedThreads[i]);
            String threadName = threadInfo.getThreadName();
            System.out.println(threadName);
        }
    }
}

trylock

ublic class TryLock implements Runnable {

    static int i = 0;
    static ReentrantLock lock = new ReentrantLock();

    @Override
    public void  run() {

        try {
            if(lock.tryLock(1000, TimeUnit.MILLISECONDS)) {
                System.out.println(Thread.currentThread().getName()+"获取锁成功了");
                try{
                    for (int j = 0; j < 1000; j++) {
                        i++;
                    }
                }catch(Exception ex){

                }finally{
                    //lock.unlock();   //释放锁
                }
            }else {
                System.out.println(Thread.currentThread().getName()+"获取锁失败了");

                //如果不能获取锁,则直接做其他事情
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }

    public static void main(String[] args) {


        Thread thread = new Thread(new TryLock());
        Thread thread1 = new Thread(new TryLock());
        thread.start();
        thread1.start();
        try {
            thread.join();
            thread1.join();

        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(i);
        System.out.println("done");


    }
}