多线程与高并发2
1、synchronized关键字
概念:多个线程访问同一个资源需要上锁。
例如两个线程同时对1进行加法运算。第一个线程+1.等于二这个2,还没写回去时候。第二线程读到了1。再在1的基础上加1导致最终结果为2.是错误的。最终结果应该是3
所以一个线程对资源要进行独占。写完以后释放资源后。另外的线程才能进入,进行读写操作。
这就是synchronized关键字作用
package com.example.demo.gc1;
public class T{
private int count = 100;
private Object o = new Object();
public void m(){
synchronized (o){ // 任何线程想要执行下面的代码必须拿到o锁
System.out.println(Thread.currentThread().getName()+"-"+count);
}
}
}
每次定义一个锁对象把他new出来太麻烦
package com.example.demo.gc2;
public class T {
private int count = 10;
public void m(){
synchronized (this){
count --;
System.out.println(Thread.currentThread().getName()+"-"+count);
}
}
}
synchronized (this)锁住当前行
package com.example.demo.gc3;
public class T {
private int count = 10;
public synchronized void m(){
count --;
System.out.println(Thread.currentThread().getName()+"-"+count);
}
}
synchronized锁对象
package com.example.demo.gc4;
public class T {
private static int count = 10;
public synchronized static void m(){
count --;
System.out.println(Thread.currentThread().getName()+"-"+count);
}
public static void mm(){
synchronized (T.class){
count --;
}
}
}
T.class针对静态方法,一般咋们的静态方法被调用。不用创建这个类对象。直接类名.方法。这里也是一个意思
synchronized (T.class)就代表锁的是T类对象
####面试题T.class是单例的吗?
一个class load到内存里是不是单例的。一般情况下是。如果是同一个classLoader空间那就是。不是同一个类加载器就不是。不同类加载器就不是单例。。。不是同一个加载器相互之间也不能访问。但是T.class能访问。那他就是单例
2、volatile内存可见性
未加锁出现的线程安全问题,来看一段代码
package com.example.demo.gc5;
public class T implements Runnable {
private int count = 100;
@Override
public void run() {
count -- ;
System.out.println(Thread.currentThread().getName()+"-"+count);
}
public static void main(String[] args) {
T t = new T();
for (int i=0;i<100;i++){
new Thread(t,"THREAD"+i).start();
}
}
}
volatile作用与变量这里。但不结合synchronized一样会有线程安全问题
package com.example.demo.gc5;
public class T implements Runnable {
private volatile int count = 100;
@Override
public void run() {
count -- ;
System.out.println(Thread.currentThread().getName()+"-"+count);
}
public static void main(String[] args) {
T t = new T();
for (int i=0;i<100;i++){
new Thread(t,"THREAD"+i).start();
}
}
}
以下两种方法可以完成线程独占
1、volatile和synchronized结合用
package com.example.demo.gc5;
public class T implements Runnable {
private volatile int count = 100;
@Override
public synchronized void run() {
count -- ;
System.out.println(Thread.currentThread().getName()+"-"+count);
}
public static void main(String[] args) {
T t = new T();
for (int i=0;i<100;i++){
new Thread(t,"THREAD"+i).start();
}
}
}
2、只用synchronized
package com.example.demo.gc5;
public class T implements Runnable {
private int count = 100;
@Override
public synchronized void run() {
count -- ;
System.out.println(Thread.currentThread().getName()+"-"+count);
}
public static void main(String[] args) {
T t = new T();
for (int i=0;i<100;i++){
new Thread(t,"THREAD"+i).start();
}
}
}
####synchronized和volatile区别?
1、volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取
2、synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
3、volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
4、volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
5、volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
6、volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化
在使用synchronized时候就可以不用volatile了
package com.example.demo.gc6;
public class T implements Runnable {
private int count = 100;
@Override
public synchronized void run() {
count --;
System.out.println(Thread.currentThread().getName()+"count = "+count);
}
public static void main(String[] args) {
T t = new T();
for (int i = 0; i < 100; i++) {
new Thread(t,"THREAD"+i).start();
}
}
}