Java线程安全和对象共享关键字

Synchronized关键字

静态的synchronized方法以class对象作为锁,又称Intrinsic Lock或Monitor锁;

Synchronized (lock) {

//访问或修改由锁保护的共享状态

}

同步代码块:对象的Monitor锁底层有monitorenter和monitorexit指令,monitorenter指令将计数器值加1,monitorexit将计数器值减1;当计数器值为0时monitor锁才能由线程获取,且计数器值只能由持有Monitor锁的线程修改。并且,无论方法正常结束还是异常终止,monitorexit指令都会执行,以确保线程释放monitor锁。

同步方法:使用的是运行时常量池中方法的 ACC_SYNCHRONIZED 标志,原理与以上类似。

示例代码:

public class CountSync implements Runnable {
    private int i = 0;

    public synchronized void increase(){
        i++;
    }
    @Override
    public void run() {
        for (int j = 0; j < 1000000; j++) {
            increase();
        }
    }

    public synchronized int getI() {
        return i;
    }

    public static void main(String[] args) throws InterruptedException {
        CountSync sync = new CountSync();
        Thread t1 = new Thread(sync);
        Thread t2 = new Thread(sync);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(sync.getI());
    }
}

注:synchronized关键字用于静态方法时使用的是类对象的锁


Volatile变量

变量的更新操作会被及时通知到其他线程,只确保可见性不保证原子性但volatile 关键宇能够保障对 long/double 型变量的写操作具有原子性。

加入volatile会禁止指令重排,强制对缓存的修改操作立即写入主内存;其他线程的读操作直接从主内存读取最新值。

使用场景:1. 对变量的写操作不依赖当前值;

                  2. 该变量没有包含在具有其他变量的不变式中。

示例代码:

public class VolaThread implements Runnable {
    public static volatile int n = 0;

    @Override
    public void run() {
        try {
            for (int i = 0; i < 100; i++) {
                increment();
                Thread.sleep(10);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void increment() {
        n++;
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new VolaThread());
        Thread t2 = new Thread(new VolaThread());
        Thread t3 = new Thread(new VolaThread());
        Thread t4 = new Thread(new VolaThread());
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t1.join();
        t2.join();
        t3.join();
        t4.join();
        System.out.println("n is :" + n);

    }
}

ThreadLocal类

ThreadLocal用于保存某个线程共享变量:对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量。使线程中的某个值与保存值的对象关联起来,ThreadLocal对象通常用于防止对可变的单实例变量或全局变量进行共享

ThreadLocal使用ThreadLocalMap内部静态类,实际调用ThreadLocalMap的set和get方法设置和获取值,key为当前ThreadLocal对象,value为变量副本。

示例代码:

import java.text.SimpleDateFormat;
import java.util.Date;

public class MyThreadLocal {
    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("HH:mm:ss SSS");
    private static final ThreadLocal<Object> threadLocal = new ThreadLocal<Object>() {
        @Override
        protected Object initialValue() {
            System.out.println("[" + DATE_FORMAT.format(new Date()) +"] " + Thread.currentThread().getName() + " invokes method initialValue, return Default value");
            return null;
        }
    };

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new MyIntegerTask("IntegerTask1"));
        Thread t2 = new Thread(new MyStringTask("StringTask1"));
        Thread t3 = new Thread(new MyIntegerTask("IntegerTask2"));
        Thread t4 = new Thread(new MyStringTask("StringTask2"));
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        //等待t1,t2,t3,t4死掉
        t1.join();
        t2.join();
        t3.join();
        t4.join();
        System.out.println("******************All done*****************");
        System.out.println("线程" + Thread.currentThread().getName() + " get variable, result " + MyThreadLocal.threadLocal.get());
        MyThreadLocal.threadLocal.remove();
    }

    public static class MyIntegerTask implements Runnable {
        private String name;

        MyIntegerTask(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                if (null == MyThreadLocal.threadLocal.get()) {
                    MyThreadLocal.threadLocal.set(0);
                    System.out.println("[" + DATE_FORMAT.format(new Date()) + "]" + " 线程" + name + ": 0");
                } else {
                    int num = (int) MyThreadLocal.threadLocal.get(); //没有抛ClassCastException
                    MyThreadLocal.threadLocal.set(num + 1);
                    System.out.println("[" + DATE_FORMAT.format(new Date()) + "]" + " 线程" + name + ":" + MyThreadLocal.threadLocal.get());
                    if (i == 3) {
                        MyThreadLocal.threadLocal.remove();
                    }
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static class MyStringTask implements Runnable {
        private String name;

        MyStringTask(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                if (null == MyThreadLocal.threadLocal.get()) {
                    MyThreadLocal.threadLocal.set("a");
                    System.out.println("[" + DATE_FORMAT.format(new Date()) + "]" + " 线程" + name + ":a");
                } else {
                    String string = (String) MyThreadLocal.threadLocal.get();
                    MyThreadLocal.threadLocal.set(string + "a");
                    System.out.println("[" + DATE_FORMAT.format(new Date()) + "]" + " 线程" + name + ":" + MyThreadLocal.threadLocal.get());
                }
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

注:1. 使用完线程共享变量后,显示调用ThreadLocalMap.remove方法清除线程共享变量;

    2. JDK建议ThreadLocal定义为private static,这样ThreadLocal弱引用的问题则不存在了


Final域

用于构造不可变对象,能确保初始化过程的安全性

示例代码:

 

参考:《Java并发编程实战》、深入理解Java并发之synchronized实现原理正确使用 Volatile 变量ThreadLocal用法详解和原理

发布了11 篇原创文章 · 获赞 1 · 访问量 1639

猜你喜欢

转载自blog.csdn.net/u011578173/article/details/91351360
今日推荐