这是我参与更文挑战的第5天,活动详情查看: 更文挑战
这篇文章我们要写的是线程间的共享。
什么是线程安全问题,当时我刚毕业,面试一家公司,有一个笔试题就问道了什么是线程安全,在开发中是否遇到线程安全的场景,举个例子?当时我一脸蒙蔽,当时对线程啥的都很模糊,只知道有线程这么个东西!所以当时做完笔试题之后就没有下文了。ε=(´ο`*)))唉
对线程的基本概念啥的还不知道的同学:Java并发编程---Java多线程基本概念
1、什么是线程间的共享
我们知道进程是操作系统进行资源分配和调度的基本单位,线程thread是操作系统能够进行运算调度的最小单位,所以在某个进程里面,进程的所有资源对于这个进程里的所有线程来说是共享的,既然是共享的,所以在并发操作里,我们的数据就很有可能不安全,上代码举例子
public class SynTest {
private long count =0;
private Object obj = new Object();//作为一个锁
public long getCount() {
return count;
}
public void setCount(long count) {
this.count = count;
}
public void incCount3(){
count++;
}
//线程
private static class Count extends Thread{
private SynTest simplOper;
public Count(SynTest simplOper) {
this.simplOper = simplOper;
}
@Override
public void run() {
for(int i=0;i<10000;i++){
simplOper.incCount();//count = count+10000
}
}
}
public static void main(String[] args) throws InterruptedException {
SynTest simplOper = new SynTest();
//启动两个线程
Count count1 = new Count(simplOper);
Count count2 = new Count(simplOper);
count1.start();
count2.start();
Thread.sleep(50);
System.out.println(simplOper.count);//20000
}
}
我们同时用两个线程对count1分别相加一万次,那么结果应该是2000,但是我们输出的结果肯定是小于2000的
这就很有可能发生一种情况,我count1这个线程拿到count的值是0,去加1,我这个操作没有做完,我count2拿到的count的值是也是0,他们同时+1,也就是说count+了两次,但是结果却是加了一次!出现了这个情况,我们对count完全失去了把控,所以我们称count在这种情况下是线程不安全的,也可以说这两个线程的操作没有同步!
为了解决这个问题,我们就引入锁这个概念!
2、synchronized内置锁
java支持多个线程同时访问一个对象或者对象的成员变量,关键字synchronized可以修饰方法或者以同步块的方式使用,它主要确保多个线程在同一时刻,只有一个线程处于方法或同步块中,保证了线程对变量访问的可见性和排他性
synchronized使用
对象锁
//用在方法上
public synchronized void incCount2(){
count++;
}
/*用在同步块上,但是锁的是当前类的对象实例*/
Object o=new Object();
public void incCount3(){
synchronized (this){
count++;
}
}
synchronized修饰方法的时候,它其实和synchronized (this){ count++; }是一样的,修饰方法的时候,它默认的对象锁就是它本身的实例,也就是this,但是在同步代码块中,this可以是其它的对象,可以是o,这个对象也就是锁。它的基本原理,就是我们锁,锁的对象头,就会有一个位置来专门标记我是否被持有,如果这个锁持有的话,标记位是1,不被持有的话就是0,我们两个线程要是同时访问这个同步代码块,就会去抢锁,谁抢到之后,就会持有这把锁,然后去访问这个同步代码块,访问完成后,锁被释放,再被其它线程抢占持有,接着执行这个代码块,保证了线程对变量访问的可见性和排他性
理解完这个我们再看一下类锁
类锁
类锁是用于类的静态方法,或者同步代码块里锁的是静态变量的对象
private static synchronized void synClass(){
System.out.println(Thread.currentThread().getName()
+"synClass going...");
SleepTools.second(1);
System.out.println(Thread.currentThread().getName()
+"synClass end");
}
private static Object obj = new Object();
private static void synStatic(){
synchronized (obj){
System.out.println(Thread.currentThread().getName()
+"synStatic going...");
SleepTools.second(1);
System.out.println(Thread.currentThread().getName()
+"synStatic end");
}
}
其实类锁也是对象锁,static synchronized void synClass()这个是锁的我们当前类在类加载的时候生成的Class对象,这个对象有且只有一个 第二个方法锁的也是一个对象就是静态的obj
3、总结
这篇介绍了什么是线程安全,以及解决线程安全问题的方法使用synchronized内置锁,大家一定要理解对象锁的本质,就不会出现类锁这个概念了!如果有不对的地方希望大佬在评论区指出,希望大佬们一键三连!