JAVA如何开启线程?
1、继承Thread类,重写run方法。
2、实现Runnable接口,实现run方法。
3、实现Callable接口,实现call方法。通过FutureTask创建一个线程,获取到线程执行的返回值。
4、通过线程池来开启线程。
怎么保证线程安全?
1、 JVM提供的锁, 也就是Synchronized关键字。
2、 JDK提供的各种锁 Lock。
Synchronized关键字,用来加锁。
Volatile只是保持变量的线程可见性。
通常适用于一个线程写,多个线程读的场景。
不能。Volatile关键字只能保证线程可见性, 不能保证原子性。
Volatile防止指令重排。在DCL中,防止高并发情况下,指令重排造成的线程安全问题。
DCL(Double Check Lock)单例:
在锁的前面后面各加一次判断(保证严格单例、双重检查锁)
指令重排:
编译器优化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序
CPU在底层创建一个对象的步骤:
分配内存 --> 对象初始化 --> 建立指针对应关系
若发生指令重排,有可能顺序改变:
分配内存 --> 建立指针对应顺序 --> 对象初始化
有可能会造成:一个线程存储一个值为8,内存是0,但执行的顺序改变了,指针先指向0,后初始话,另一个线程调用时,调用的数值不是8,而是0
偏向锁:只有一个线程进入临界区;
轻量级锁:多个线程交替进入临界区;
重量级锁:多个线程同时进入临界区。
偏向所锁,轻量级锁都是乐观锁,重量级锁是悲观锁。
一个对象刚开始实例化的时候,没有任何线程来访问它的时候,它是可偏向的;
线程来访问它的时候,它会偏向这个线程,此时,对象持有偏向锁;
一旦有第二个线程访问这个对象,因为偏向锁不会主动释放,所以第二个线程可以看到对象时偏向状态,这时表明在这个对象上已经存在竞争了,检查原来持有该对象锁的线程是否依然存活,如果挂了,则可以将对象变为无锁状态,然后重新偏向新的线程,如果原来的线程依然存活,则马上执行那个线程的操作栈,检查该对象的使用情况,如果仍然需要持有偏向锁,则偏向锁升级为轻量级锁。如果不存在使用了,则可以将对象回复成无锁状态,然后重新偏向;
轻量级锁认为竞争存在,但是竞争的程度很轻,一般两个线程对于同一个锁的操作都会错开,或者说稍微等待一下(自旋),另一个线程就会释放锁。 但是当自旋超过一定的次数,或者一个线程在持有锁,一个在自旋,又有第三个来访时,轻量级锁膨胀为重量级锁,重量级锁使除了拥有锁的线程以外的线程都阻塞,防止CPU空转。
原文:https://www.cnblogs.com/chilly-night/p/14947811.html