同步的进一步了解

一、同步函数

同步函数:用同步关键字(synchronized)对函数进行修饰。则函数具有了同步性。
同步表现形式有两种:
1,同步代码块
2,同步函数

二、同步函数的锁

同步函数需要被对象调用,调用的对象不确定,用this来表示。同步函数使用的锁就是this。

验证同步函数使用的锁是this
验证思路:启动两个线程,一个线程负责执行同步代码块(使用明锁),另一个线程负责执行同步函数(使用this锁)。
    两个线程执行相同的任务,如果没有使用相同的锁,说明他们没有同步,会出现数据错误。

验证实例代码:
class SaleTicket implements Runnable
{
	private int tickets = 100;
	//定义一个boolean类型标记,使一个线程一直在同步代码块中,另一个线程一直在同步函数中
	private boolean flag = true;
	Object obj = new Object();
	public void run(){
		if(flag)
			//让线程循环,执行卖票动作
			while(true){
				//此时使用的锁是obj,根据结果发现安全问题仍然存在,如果把obj换成this。说明同步函数中使用的锁是this
				//synchronized(this){
				synchronized(obj){
					if(tickets>0){
						try{Thread.sleep(10);}catch(InterruptedException e){}
						System.out.println(Thread.currentThread().getName()+".....code...."+tickets--);
					}
				}
			}
		else
			while(true)
				sale();
	}
	public synchronized void sale(){
		if(tickets>0){
						try{Thread.sleep(10);}catch(InterruptedException e){}
						System.out.println(Thread.currentThread().getName()+".....func...."+tickets--);
					}
	}
}

class	ThisLockDomo{
	public static void main(String[] args) throws InterruptedException {
		SaleTicket t = new SaleTicket();
		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);

		t1.start();//此时不一定立即执行,
		//为了保证线程之间的切换,在flag置为false之前,一定要使t1执行。
		Thread.sleep(10);
		t.flag = false;
		t2.start();
	}
}

三、static同步函数的锁

static同步函数的锁是  类名.class

由于静态函数中不存在this。所以static同步函数的锁肯定不是this。
在这里要提到几个概念:
1、字节码文件对象:java会把字节码文件封装在对象,这个对象就是字节码对象。
2、任何数据类型都具备静态属性class
3、类是描述事物的,在java中用Class来描述类,Class可以理解为描述类的类。

static方法随着类的加载,这时不一定有该类的对象,但是一定有一个该类的字节码文件对象。这个对象简单的表示方式就是   类名.class ,字节码文件对象的类型是Class

可以通过修改标题二中的实例对其进行验证
以this作为同步代码块中的锁,数据安全问题存在。
以对应字节码对象( SaleTicket .class)作为同步代码块中的锁,数据安全问题解决。

四、单例模式的并发访问

1、饿汉式:相对与线程并发是安全的

//线程任务
class Demo implements Runnable
{
	public void run(){
		Single.getInstance();
	}
}

//饿汉式	有共享数据,但没有多条代码对共享数据进行操作,线程是安全的
class Single
{
	private static final Single SINGLE_INSTANCE = new Single();
	private Single(){}
	public static Single getInstance(){
		return SINGLE_INSTANCE;
	}
}

2、懒汉式:相对与线程并发是不安全的。

//懒汉式  其中s为并发中的共享数据,并对s进行了两次运算。线程是不安全的。
class Single
{
	private static Single s = null;
	private Single(){}
	public static Single getInstance(){
		if(s==null)
			s = new Single();
		return s;
	}
}

3、解决懒汉式中的线程安全问题

//在多线程并发访问时,会出现线程安全问题,加同步可以解决问题,同步函数和同步代码块都可以,
//但是,由于判断逻辑增加,效率降低,可以通过if对单例对象的双重判断的形式提高效率。如下
class Single
{
	private static Single s = null;
	private Single(){}
	public static Single getInstance(){
		if(s==null){
			synchronized(Single.class){
				if(s==null)
					s = new Single();
			}
		}
		return s;
	}
}

五、同步函数和同步代码块的区别

区别:
同步代码块可以使用任意对象作为锁
同步函数只有使用this作为锁

如何选择:
如果说,一个类中只需要一个锁,这时可以考虑同步函数,使用this,写法简单。
如果说,一个类中需要多个锁,还有多个类中使用同一个锁,这时只能使用同步代码块。

六、死锁

对于死锁的概念就先不生硬的说了。现在来看一个关于死锁的实例。或许通过下面的例子你能够理解死锁的概念。在例子中定义了两把锁,并使用flag标记使两个线程进入不同的区域执行,当执行的时候,线程t1拿到LOCKA,等待获取LOCKB,线程t2拿到LOCKB,等待获取LOCKA,此时两个线程都无法执行下去。

这种死锁是同步嵌套导致的。所以在写程序时要避免同步的嵌套。
//定义线程任务
class Task implements Runnable
{
	private boolean flag;
	Task(flag){
		this.flag = flag;
	}

	public void run(){
		if(flag){
			while(true)
				synchronized(MyLock.LOCKA){
					System.out.println("if.....locka");
					synchronized(MyLock.LOCKB){
						System.out.println("if.....lockb");
					}
				}
		}
		else{
			while(true)
				synchronized(MyLock.LOCKB){
					System.out.println("else.....lockb");
					synchronized(MyLock.LOCKA){
						System.out.println("else.....locka");
					}
				}
		}
	}
}
//定义锁
class MyLock
{
	public static final Object LACKA = new Object();
	public static final Object LACKB = new Object();

}
//死锁测试类
class DeadLockTest
{
	public static void main(String[] args){
		//创建线程任务
		Task t1 = new Task(true);
		Task t2 = new Task(false);
		//创建线程并开启
		new Thread(t1).start();
		new Thread(t2).start();
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值