线程安全概念:当多个线程访问某一个类(对象或方法)时,这个类始终都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的。
线程安全来说,需要满足以下两个特性:
原子性
可见性
public class MyThread extends Thread {
    private int count = 5;
    //synchronized加锁 同步锁
    public /*synchronized*/ void run () {
        System.out.printf("线程%s执行,count:%s\n", this.currentThread().getName(),--count);
    }
    public static void main(String[] args) {
        /**
         * 当多个线程访问 MyThread 的 run 方法时,以排队的方式进行处理(这里的排序是按照 CPU 分配的先后顺序而定的)
         * 一个线程要执行 synchronized 修饰的方法时
         *  1. 尝试获得锁,如果拿到锁则执行该方法
         *  2. 这个线程就会不断尝试获得这把锁,直到拿到为止,而且是多个线程同时去竞争这把锁。(也就是会有锁竞争的问题)
         */
        MyThread thread = new MyThread();
        new Thread(thread, "t1").start();
        new Thread(thread, "t2").start();
        new Thread(thread, "t3").start();
        new Thread(thread, "t4").start();
        new Thread(thread, "t5").start();
        new Thread(thread, "t6").start();
    }
}执行结果:
//MyThread 的 run() 方法不加锁 synchronized
线程t1执行,count:4
线程t4执行,count:1
线程t5执行,count:1
线程t3执行,count:2
线程t2执行,count:3
//run() 加锁 synchronized,期待的结果 
线程t1执行,count:4
线程t2执行,count:3
线程t5执行,count:2
线程t4执行,count:1
线程t3执行,count:0由此可见:
多个线程多个锁,多个线程,每个线程都可以拿到自己指定的锁,分别获得锁后执行 synchronized 修辞的方法。
同步(synchronized)的概念就是共享,我们要牢牢记住“共享”这两个字,如果不是共享的资源,就没有必要进行同步。
异步(asynchronized)的概念就是独立,相互之间不受任何制约。eg: Ajax
public class MutiThread {
    private /*static*/ int num = 0;
    /**
     * synchronized:对象锁,两个对象,线程获得的就是两个不同的锁,互不影响
     * static synchronized:表示类级别的锁,即便多个对象也是相同的锁
     */
    public /*static*/ synchronized void printNum (String tag) {
        try {
            if("a".equalsIgnoreCase(tag)) {
                System.out.printf("tag %s 设置成功\n", tag);
                num = 100;
                Thread.sleep(1000);
            } else {
                System.out.printf("tag %s 设置成功\n", tag);
                num = 200;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.printf("tag %s , num=%s\n", tag, num);
    }
    public static void main(String[] args) {
        
        final MutiThread m1 = new MutiThread();
        final MutiThread m2 = new MutiThread();
        new Thread(new Runnable() {
            public void run() {
                m1.printNum("a");
            }
        }).start();
        new Thread(new Runnable() {
            public void run() {
                m1.printNum("b");
            }
        }).start();
    }
}执行结果:
//run()方法用synchronized修辞
tag b 设置成功
tag b , num=200
tag a 设置成功
tag a , num=100
//run()方法用static synchronized修辞
tag a 设置成功
tag a , num=100
tag b 设置成功
tag b , num=200由此可见:
static synchronized 修辞的方法,属于类级别的锁,多个对象共享同一把锁。
synchronized 修辞的方法,属于对象锁,一个对象一把锁。
对于对象的同步和异步的方法,一定要考虑问题的整体性,不然就会出现数据不一致的错误,很经典的错误就是脏读(dirtyread)
public class DirtyRead {
    private String username;
    private String password;
    public /*synchronized*/ void setValue(String username, String password) {
        this.username = username;
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            //e.printStackTrace();
        }
        this.password = password;
        System.out.printf("username=%s; password=%s\n", username, password);
    }
    public /*synchronized*/ void getValue() {
        System.out.printf("username=%s; password=%s\n", username, password);
    }
    public static void main(String[] args) throws InterruptedException {
        final DirtyRead dirtyRead = new DirtyRead();
        new Thread(new Runnable() {
            public void run() {
                dirtyRead.setValue("admin", "admin");
            }
        }).start();
        Thread.sleep(1000);
        dirtyRead.getValue();
        //username=admin; password=null 不加锁,产生脏读
        //username=admin; password=admin 加锁
    }
}执行结果:
setValue() 执行要 2s,而主程序 1s 时调用 getValue() ,这时username 已经赋值,而 password 仍未赋值,产生了脏读,username=admin; password=null
setValue() 和 getValue() 都对 username 和 password 操作,所以要避免产生脏读,需要对这两个方法都加锁 synchronized。
每天用心记录一点点。内容也许不重要,但习惯很重要!
原文:https://www.cnblogs.com/binarylei/p/8999677.html