Java并发编程---线程间的共享

482 阅读4分钟

这是我参与更文挑战的第5天,活动详情查看: 更文挑战

这篇文章我们要写的是线程间的共享。

什么是线程安全问题,当时我刚毕业,面试一家公司,有一个笔试题就问道了什么是线程安全,在开发中是否遇到线程安全的场景,举个例子?当时我一脸蒙蔽,当时对线程啥的都很模糊,只知道有线程这么个东西!所以当时做完笔试题之后就没有下文了。ε=(´ο`*)))唉

1622895374(1).jpg

对线程的基本概念啥的还不知道的同学: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的

image.png 这就很有可能发生一种情况,我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内置锁,大家一定要理解对象锁的本质,就不会出现类锁这个概念了!如果有不对的地方希望大佬在评论区指出,希望大佬们一键三连!