3、线程安全_synchronized

线程安全性概念:

一个类或程序在多线程环境下,其行为与预期结果一致的特性。

 

何时会产生线性安全问题?

当多个线程同时访问临界资源时(一个对象、一个文件、一个数据库、对象的属性)会导致程序运行结果与预期结果不同,即出现线性安全问题。

临界资源:在一段时间内只允许一个线程访问的资源

注:不过,当多个线程执行一个方法,方法内部的局部变量并不是临界资源,因为方法是在栈上执行的,而Java栈是线程私有的,因此不会产生线程安全问题。

 

如何解决线性安全问题?

大多数并发模式在解决线性安全问题时,都采用“序列化访问临界资源”(时间片轮询方式),即同一时刻只能一个线程访问临界资源,也称为同步互斥访问。

实现方式:在访问临界资源的代码前面加一个锁(synchronized['sɪŋkrənaɪzd]Lock,当访问完临界资源后释放锁,让其他线程访问。

采用synchronized修饰符实现的同步机制叫做互斥锁机制,它所获得的锁叫做互斥锁


Synchronized同步方法或同步块

synchronized标记一个方法或者代码块,当某个线程访问该对象的synchronized方法或者代码块时,这个线程获得了该对象的锁,其他线程暂时无法访问该方法,只能等待这个方法执行完毕或代码块执行完毕,这个线程才会释放该对象的锁,其他线程就可以访问该方法或代码

特点

1)当一个线程正在访问一个对象的synchronized方法,那么其他线程不能访问该对象的其他synchronized方法。这个原因很简单,因为一个对象只有一把锁,当一个线程获取了该对象的锁之后,其他线程无法获取该对象的锁,所以无法访问该对象的其他synchronized方法。

2)当一个线程正在访问一个对象的synchronized方法,那么其他线程能访问该对象的非synchronized方法。这个原因很简单,访问非synchronized方法不需要获得该对象的锁,假如一个方法没用synchronized关键字修饰,说明它不会使用到临界资源,那么其他线程是可以访问这个方法的,

3)如果一个线程A需要访问对象object1synchronized方法fun1,另外一个线程B需要访问对象object2synchronized方法fun1,即使object1object2是同一类型),也不会产生线程安全问题,因为他们访问的是不同的对象,所以不存在互斥问题。

package com.cdd.dao;

import java.util.ArrayList;
import java.util.List;

public class InsertData {
	private List<Integer> list = new ArrayList<Integer>();
	private Object object = new Object();
	
	//未加锁
	public void insert(Thread thread){
		for(int i=0;i<5;i++){
			System.out.println(thread.getName()+"插入数据"+i);
			list.add(i);
		}
	}
	//synchronized同步方法
	public  synchronized void insert0(Thread thread){
		for(int i=0;i<5;i++){
			System.out.println(thread.getName()+"插入数据"+i);
			list.add(i);
		}
	}
	//另一个synchronized同步方法.当线程在访问一个对象的synchronized方法时,那么其他线程不能访问该对象的其他synchronized方法。(可以访问非synchronized方法)
	//因为一个对象只有一个锁,当一个线程获取该对象的锁之后,其他线程无法获取该对象的锁,所以无法访问该对象其他synchronized方法
	public synchronized void print(Thread thread){
		System.out.println(thread.getName()+"访问");
	}
	//synchronized同步代码块
	public void insert1(Thread thread){
		synchronized (this) {
			for(int i=0;i<5;i++){
				System.out.println(thread.getName()+"插入数据"+i);
				list.add(i);
			}
		}
	}
	
	public void insert2(Thread thread){
		synchronized (object) {
			for(int i=0;i<5;i++){
				System.out.println(thread.getName()+"插入数据"+i);
				list.add(i);
			}
		}
	}
}
//测试
<pre name="code" class="java">public static void main(String[] args) {
		final InsertData insertData = new InsertData();
		new Thread(){
			public void run(){
				insertData.insert0(Thread.currentThread());
			}
		}.start();
		
		new Thread(){
			public void run(){
				insertData.print(Thread.currentThread());
			}
		}.start();
	}
 

每个类也会有一个锁,它可以用来控制对static数据成员的并发访问(例如:单例模式)

Synchronized同步方法或代码块获取锁线程释放锁的两种情况:

1、获取锁的线程执行完毕,释放对锁的占用

2对于synchronized方法或者synchronized代码块,当出现异常时,JVM会自动释放当前线程占用的锁,因此不会由于异常导致出现死锁现象。


互斥是实现同步的一种手段,临界区、互斥量和信号量都是主要的互斥实现方式synchronized关键字经过编译后,会在同步块的前后分别形成monitorentermonitorexit这两个字节码指令。根据虚拟机规范的要求,在执行monitorenter指令时,首先要尝试获取对象的锁,如果获得了锁,把锁的计数器加1,相应地,在执行monitorexit指令时会将锁计数器减1,当计数器为0时,锁便被释放了。由于synchronized同步块对同一个线程是可重入的,因此一个线程可以多次获得同一个对象的互斥锁,同样,要释放相应次数的该互斥锁,才能最终释放掉该锁。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值