Chapter VI concurrent security

What is the thread safety? In the "Java Concurrency in combat", the definition is as follows:
when multiple threads access a class, no matter what kind of scheduling runtime environment using or how these threads will alternate execution, and does not require any additional synchronization in the calling code or in cooperation with, the class can show the correct behavior, then call this class is thread-safe.

A thread closed

Achieve good concurrency is a difficult thing, so many times we want to avoid concurrency. The easiest way to avoid concurrent threads is closed. What is the thread closed it? The object is to encapsulate a thread, a thread can only see this object. So even if the object is not thread safe and will not have any security problems. What are some ways to achieve thread closed it?

ad-hoc thread closed:
it is totally controlled by the thread implementers closed his thread closed fully realized by the implementor. Ad-hoc thread closed very fragile and should be avoided if possible.

Closed stack:
the stack is closed most of thread programming we encounter them closed. What is the stack closed it? Simply means that local variables. A plurality of threads access method, which will be the local variable stack copy to the thread. So the local variables will not be shared by multiple threads, it will not appear concurrency issues. So do not use local variables using global variables, global variables easily lead to concurrency issues.

Class 1, stateless

No class member variable called stateless class, this class must be thread-safe. If the method parameters used in this class of objects is thread safe? such as:

public class StatelessClass {
	public int service(int a,int b){
	    return a+b;
    }
    public void serviceUser(UserVo user){
        //do sth user
    }
}

Of course, because the use of multi-threaded, although the user of this object instances are not normal, but for StatelessClass object instance of this class, it does not hold the object instance UserVo, which he does not have problems, there the problem is UserVo this class, rather than StatelessClass itself.

2, let the class immutable

Let immutable state, in two ways:

  • Plus final keyword for a class, all member variables should be private, the same as long as possible, all member variables should be added to the final keyword, but with final, should pay attention if the member is a variable object this object corresponding class should not be changed, in order to ensure that the entire class is immutable. See the code:
   public class ImmutableClass {
    	private final int a;
    	public int getA() {
    		return a;
    	}
    	public UserVo getUser() {
    		return user;
    	}
    	public ImmutableClass(int a) {
    		this.a = a;
    	}
	public static class User{
    	private int age;
		public int getAge() {
			return age;
		}
		public void setAge(int age) {
			this.age = age;
		}
    }  
}
  • It did not provide any places to modify member variables, member variables at the same time as the method does not return a value. See the code:
   public class ImmutableClassToo {
    	private final List<Integer> list = new ArrayList<>(3);
    	public ImmutableClassToo() {
    		list.add(1);
    	    list.add(2);
    	    list.add(3);
    	}
    	public boolean isContain(int i){
    		return list.contains(i);
    	 }
    }

Note, however, once the member variable class objects have the assurance of the final keyword immutable and can not guarantee the security of the class, why? Since in multi-threaded, although the referenced object is not changed, but in the example of heap objects are likely to be modified in a plurality of threads simultaneously, there is no case where the correct processing of data objects in the heap are instances unpredictable. This brings us to how to safely release the object of this issue.

public class ImmutableClass {
	private final int a;
	private final UserVo user = new UserVo(); //不安全
	 
	public int getA() {
		return a;
	}
	public UserVo getUser() {
		return user;
	}
	public ImmutableClass(int a) {
		this.a = a;
	}
	
	public static class User{
    	private int age;
		public int getAge() {
			return age;
		}
		public void setAge(int age) {
			this.age = age;
		}
    }  
}
  • volatile way: does not guarantee thread safety class, we can only guarantee the visibility of the class, the most suitable one thread to write, read multiple threads scenario.
  • Locking and CAS: guarantee thread-safe means we use most often, the use of the synchronized keyword, use explicit locks, using a variety of atomic variables, use the CAS mechanism and so when modifying data.

3, secure publishing

Class member variables held, if the basic types, advertised, and it does not matter, because advertised is actually a copy of this variable, see the code:

public class SafePublish {
	private int i;
    public SafePublish() {
    	i = 2;
    }
	public int getI() {
		return i;
	}
	public static void main(String[] args) {
		SafePublish safePublish = new SafePublish();
		int j = safePublish.getI();
		System.out.println("before j="+j);
		j = 3;
		System.out.println("after j="+j);
		System.out.println("getI = "+safePublish.getI());
	}
}

However, if a class member variable holds a reference to an object is, if the member object is not thread safe, get advertised through other methods, will result in a multi-threaded incorrectly modify the data held by the members of the object itself, which cause a problem entire class thread safe. See the following code can be seen:

public class UnSafePublish {
	private List<Integer> list = new ArrayList<>(3);
    public UnSafePublish() {
    	list.add(1);
    	list.add(2);
    	list.add(3);
    }
	public List getList() {
		return list;
	}

	public static void main(String[] args) {
		UnSafePublish unSafePublish = new UnSafePublish();
		List<Integer> list = unSafePublish.getList();
		System.out.println(list);
		list.add(4);
		System.out.println(list);
		System.out.println(unSafePublish.getList());
	}
}

After the release of this list out, it can be modified between the external thread, in the case of multiple threads to modify insecurity is certainly exist, and how to fix this problem? When we release this object, the object should be packed with a thread-safe manner. We will list later packaged with Collections.synchronizedList, no matter how many threads to use this list, it is thread safe.

private List<Integer> list = Collections.synchronizedList(new ArrayList<>(3));

For our own use or class declaration, JDK there will be no way to provide this kind of packaging, but we can mimic this pattern or to delegate class thread-safe, of course, release of such objects get out by other methods, most the fundamental solution is to be achieved in the thread-safety issues in mind.

TheadLocal way:

ThreadLocal is the best way to achieve thread closed. ThreadLocal internal maintains a Map, Map of the key is the name of each thread, while the value of the Map is what we object to is closed. Each thread in the Map object corresponds to a value, that is, the use of ThreadLocal Map implementation thread closed object.

Servlet Analysis

Not thread-safe class, why do not we usually feel:
1, on demand, there is little sharing needs.
2, has received the request and returns the response time, usually by one thread responsible. But as long as there Servlet member variables, how to write once in a thread, it is easy to produce thread-safety issues.

Second, deadlock

1, the concept of deadlock

Deadlock refers to two or more processes in the implementation process, due to the competition for resources or A blocking phenomenon caused due communicate with each other, without external force, they will not be able to promote it. At this time, say the system is in deadlock state or system to produce a deadlock.

For Example: A and B to massage the feet, feet when wanted, the way to be at the same time a head massage, good foot massage technician 13, 14 good head massage. A first this time to grab 14, B 13 to grab the first two people want to feet and head massage at the same time, so he refused to give you threatened my dead body, this is the case, grabbed 14 A, want 13, B 13 to grab, I want 14 feet and at the same time want to do on head massage a and B produces a deadlock. How to solve this problem?
First, if this time, to a 15, also happens to be good at head massage, A and no two heads, naturally go to B, then B will make flattered feet and head massage, and the remaining A next grumpy, this time the deadlock was broken this case, does not exist.
The second, C appearance, and the use of force to force A and B, and feet must do first, and then head massage, in this case, A and B, who first grabbed the 13, anyone can go on, another did not grab to, waiting for, in this case, it will not deadlock.

So to sum up:
Deadlock is inevitable in a multi-operator (M> = 2 th), the competition for resources plurality (N> = 2 th, and N <= M) this situation will occur. Obviously, there will be single-threaded nature of deadlock, only to a B, not two, played ten no problem; single resources? Only 13, A and B will only generate fierce competition, battle it, who grab is who, but no deadlock. At the same time, there is a deadlock important requirement, the order does not compete for resources, competition for resources if the order is the same, it will not deadlock.

2, academic definitions

Deadlock occurs must meet the following four requirements:

  • 1) exclusive conditions: refers to the process of the allocated resources to be exclusive use of a resource that is occupied by only one process at a time. If at this time there are other resources to process the request, the requestor can only wait until the share of resources used up in the process of release.
  • 2) request and keeping conditions: that the process has to keep at least one resource, but proposed a new resource request, the resource has been occupied by other processes, this time requesting process blocked, but they have other resources available remain put.
  • 3) does not deprive condition: Resource refers to the process have been obtained before is not used, can not be denied, can only be released by themselves when you are finished.
  • 4) Loop wait condition: refers to a deadlock occurs, there is a necessary process - endless chain of resources, i.e., the process set {P0, P1, P2, ···, Pn} of waiting for an P0 P1 occupies resources; P1 P2 is waiting for resources occupied, ......, Pn is waiting for a resource that has been occupied by P0.

Understand the reason for the deadlock, especially four necessary conditions for deadlock, it were possible to avoid, prevent and relieve the deadlock. As long as to break one of the four necessary conditions for effective in preventing the occurrence of deadlock:

  • Break mutually exclusive conditions: the transformation of exclusive resources to virtual resources, most of the resources has been unable to reform.

  • Not seize break condition: When a process exclusive possession of a resource and then apply for a exclusive resources can not be met, then exit the resources of the original possession.

  • Possession of break and eligibility criteria: the use of pre-allocated resource strategy, namely to apply all the resources before running the process to meet the run, or to wait, so you do not possess and apply.

  • Wait for conditions to break the cycle: to achieve an orderly distribution of resources strategies to achieve the classification number for all devices, all processes can only be used by increasing numbers form of application resources.

  • Deadlock avoidance algorithm has ordered common resource allocation method, bankers algorithm.

3, phenomenon, hazards and solutions

In the IT world we have a situation there is no deadlock, are: multi-database transactions but also to the case of simultaneous operation of multiple tables. Therefore, the database design time to take into account the deadlock detection and recovery mechanisms from the deadlock. For example, oracle provides deadlock detection and handling statement, mysql also provides a "cycle-dependent mechanism detects the"
Here Insert Picture Description
Here Insert Picture Description
existence of multiple threads compete for more resources in the Java world, inevitably there is a deadlock. Then we will happen under what circumstances it when writing code?

3.1 phenomenon

Simple sequence deadlock, see Code NormalDeadLock:

// 先拿第一个锁,再拿第二个锁
private static void fisrtToSecond() throws InterruptedException {
	String threadName = Thread.currentThread().getName();
	synchronized(valueFirst) {
		System.out.println(threadName + "get first  lock end!");
		Thread.sleep(100);
		synchronized(valueSecond) {
			System.out.println(threadName+" get second lock end!");
		}
	}
}
//先拿第二个锁,再拿第一个锁(产生死锁,可以通过调整锁的顺序解决)
private static void SecondToFisrt() throws InterruptedException {
    String threadName = Thread.currentThread().getName();
    synchronized (valueFirst){
        System.out.println(threadName+" get first lock end");
        Thread.sleep(100);
        synchronized (valueSecond){
            System.out.println(threadName+" get second lock  end");
        }
    }
}

Dynamic deadlock sequence: As the name suggests order of acquisition and lock are related, but more subtle, unlike the simple sequence deadlock, often at a glance from the code sequence does not acquire a lock. See the code com.chj.thread.capt08.transfer.UserAccount.

3.2 Hazard

  • Thread does not work, but the entire program is still alive.
  • Without any exception information can be used for us to check.
  • Once the program deadlock occurs, there is no way to recover, only to restart the program, the production platform program, this is a very serious problem.

The actual work of deadlock indefinite periods of time, each will not now; the event without any exception information, just know that all business applications more slowly and finally stop the service, can not determine which specific business problem is caused; test department We can not reproduce, concurrency is not enough.

3.3 solve

Positioning: To resolve the deadlock, of course, need to find a deadlock, how to find?
First, open the console, cd C: \ Program Files \ Java \ jdk1.8.0_131 \ by jps query applications bin directory id, then by jstack id holdings to view the application locks:
Here Insert Picture Description
Here Insert Picture Description

3.4 Modify

The key is to ensure consistency order to get the lock, two solutions:
1) internal comparison by order, determine the order to take the lock;
2) the use of mechanisms to try to get the lock. See the code:

public class SafeOperate implements ITransfer{
	// 第三把锁
	private static Object tieLock = new Object();
	@Override
	public void transfer(UserAccount from, UserAccount to, int amount) throws InterruptedException {
		int fromHash = System.identityHashCode(from);
		int toHash = System.identityHashCode(to);
		if(fromHash < toHash) {
			synchronized(from) {
				System.out.println(Thread.currentThread().getName()+" get "+from.getName());
				Thread.sleep(100);
                synchronized (to){
                    System.out.println(Thread.currentThread().getName()+" get "+to.getName());
                    from.reduceMoney(amount);
                    to.addMoney(amount);
                    System.out.println("from=="+from);
                    System.out.println("to==="+to);
                }
			}
		}else if(fromHash > toHash) {
			synchronized(to) {
				System.out.println(Thread.currentThread().getName()+" get "+to.getName());
				Thread.sleep(100);
                synchronized (from){
                    System.out.println(Thread.currentThread().getName()+" get "+from.getName());
                    from.reduceMoney(amount);
                    to.addMoney(amount);
                    System.out.println("from=="+from);
                    System.out.println("to==="+to);
                }
			}
		}else {
			synchronized(tieLock) {
				synchronized(from) {
					synchronized(to) {
						 from.reduceMoney(amount);
		                 to.addMoney(amount);
		                 System.out.println("from=="+from);
		                 System.out.println("to==="+to);
					}
				}
			}
		}
	}
}

And SafeOperateToo:

public class SafeOperateToo implements ITransfer {
	@Override
	public void transfer(UserAccount from, UserAccount to, int amount) throws InterruptedException {
		Random r = new Random();
		while(true) {
			if(from.getLock().tryLock()) {
				System.out.println(Thread.currentThread().getName() +" get"+from.getName());
				try {
					 System.out.println(Thread.currentThread().getName() +" get"+to.getName());
                     from.reduceMoney(amount);
                     to.addMoney(amount);
                     System.out.println(from);
                     System.out.println(to);
                     break;
				}finally {
					from.getLock().unlock();
				}
			}
		}
	}
}

Third, other security issues

1, Livelock

两个线程在尝试拿锁的机制中,发生多个线程之间互相谦让,不断发生同一个线程总是拿到同一把锁,在尝试拿另一把锁时因为拿不到,而将本来已经持有的锁释放的过程。解决办法:每个线程休眠随机数,错开拿锁的时间。

  • 线程饥饿:低优先级的线程,总是拿不到执行时间。
  • 并发下的性能:使用并发的目标是为了提高性能,引入多线程后,其实会引入额外的开销,如线程之间的协调、增加的上下文切换,线程的创建和销毁,线程的调度等等。过度的使用和不恰当的使用,会导致多线程程序甚至比单线程还要低。
  • 衡量应用的程序的性能:服务时间,延迟时间,吞吐量,可伸缩性等等,其中服务时间,延迟时间(多快),吞吐量(处理能力的指标,完成工作的多少)。多快和多少,完全独立,甚至是相互矛盾的。
  • 对服务器应用来说:多少(可伸缩性,吞吐量)这个方面比多快更受重视。

我们做应用的时候:
1)先保证程序正确,确实达不到要求的时候,再提高速度。(黄金原则)
2)一定要以测试为基准。

2、线程引入的开销

2.1 上下文切换

如果主线程是唯一的线程,那么它基本上不会被调度出去。另一方面,如果可运行的线程数大于CPU的数量,那么操作系统最终会将某个正在运行的线程调度出来,从而使其他线程能够使用CPU。这将导致一次上下文切换,在这个过程中将保存当前运行线程的执行上下文,并将新调度进来的线程的执行上下文设置为当前上下文。上下文切换有点像我们同时阅读几本书,在来回切换书本的同时我们需要记住每本书当前读到的页码。

切换上下文需要一定的开销,而在线程调度过程中需要访问由操作系统和JVM共享的数据结构。应用程序、操作系统以及JVM都使用一组相同的CPU。在JVM和操作系统的代码中消耗的CPU时钟周期越多,应用程序的可用CPU时钟周期就越少,但上下文切换的开销并不只是包含JVM和操作系统的开销。当一个新的线程被切换进来时,它所需要的数据可能不在当前处理器的本地缓存中,因此上下文切换将导致一些缓存缺失,因而线程在首次调度运行时会更加缓慢。

当线程由于等待某个发生竞争的锁而被阻塞时,JVM通常会将这个线程挂起,并允许它被交换出去。如果线程频繁地发生阻塞,那么它们将无法使用完整的调度时间片。在程序中发生越多的阻塞(包括阻塞IO,等待获取发生竞争的锁,或者在条件变量上等待)与CPU密集型的程序就会发生越多的上下文切换,从而增加调度开销,并因此而降低吞吐量。

上下文切换是计算密集型操作。也就是说,它需要相当可观的处理器时间。所以上下文切换对系统来说意味着消耗大量的CPU时间,事实上可能是操作系统中时间消耗最大的操作。上下文切换的实际开销会随着平台的不同而变化,然而按照经验来看,在大多数通用的处理器中上下文切换的开销相当于50~10000个时钟周期,也就是几微秒。

UNIX系统的 vmstat命令能报告上下文切换次数以及在内核中执行时间所占比例等信息。如果内核占用率较高(超过10%),那么通常表示调度活动发生得很频繁,这很可能是由IO或竞争锁导致的阻塞引起的。

2.2 内存同步

同步操作的性能开销包括多个方面,在 synchronized和 volatile提供的可见性保证中可能会使用一些特殊指令,即内存栅栏( Memory Barrier)。内存栅栏可以刷新缓存,使缓存无效刷新硬件的写缓冲以及停止执行管道。内存栅栏可能同样会对性能带来间接的影响,因为它们将抑制一些编译器优化操作。在内存栅栏中大多数操作都是不能被重排序的。

2.3 阻塞

Causes of obstruction include: blocking IO waiting to acquire the contended lock, or wait on the condition variable, and so on.
[Blockage causes the thread to hang suspended: suspended process can be defined as the process of being eliminated from the memory in the operating system, the machine's resources are limited, in the absence of sufficient resources, the operating system program in memory reasonable arrangement, some progress was temporarily removed from the memory, when conditions permit, the operating system will be transferred back to memory again, re-enter the state waiting to be executed that is ready state, the system is not at any time exceed a certain action】.
Obviously this operation includes at least two additional context switches, as well as the operating system level operations, etc. associated.

3, thread-safe singleton

3.1 double-checked locking:

public class SingletonDoubleCheck {
	// private volatile static SingletonDoubleCheck sDoubleCheck;
	private static SingletonDoubleCheck sDoubleCheck;
	// 私有化
	private SingletonDoubleCheck() {}

	public static SingletonDoubleCheck getInstance() {
		//第一次检查,不加锁
		if(sDoubleCheck == null) {
			System.out.println(Thread.currentThread()+" is null");
			synchronized(SingletonDoubleCheck.class) {
				//第二次检查,加锁情况下
				if(sDoubleCheck == null) {
					 System.out.println(Thread.currentThread()+" is null");
	                 //内存中分配空间  1
	                 //空间初始化 2
	                 //把这个空间的地址给我们的引用  3
					sDoubleCheck = new SingletonDoubleCheck();
				}
			}
		}
		return sDoubleCheck;
	}
}

Solutions, plus the volatile keyword
solution:

  • Lazy type: class initialization mode, also called delayed placeholder mode. In singleton class internally by a private static class to hold the internal singleton instance of the class.
    Placeholder delay mode may also be used in the assignment delay multithreaded instance fields.
public class InstanceLazy {
	private Integer value;
	private Integer heavy;//成员变量很耗资源, ;
    public InstanceLazy(Integer value) {
		super();
		this.value = value;
	}
    public Integer getValue() {
		return value;
	}
	public Integer getVal() {
		return InstanceHolder.val;
	}
	
	private static class InstanceHolder{
        public static Integer val = new Integer(100);
    }
}
  • Hungry man style: when it was declared on the new instance of this class, because in the JVM, the class load and class initialization, by the virtual machine to ensure thread safety.
  • Well-fed type:
public class LazySingletonSynchronized {
	// 通过private修饰,就是为了防止这个类被随意创建
	private LazySingletonSynchronized(){
		System.out.println("LazySingleton is created!!");
	}
	// 首先instance对象必须是private并且static,如果不是private那么instance的安全无法得到保证
	// 其次因为工厂方法必须是static,因此变量instance也必须是static
	private static LazySingletonSynchronized instance = null;
	// 为了防止对象被多次创建,使用synchronized关键字进行同步(缺点是并发环境下加锁,竞争激励的场合可能对性能产生一定影响)
	public static synchronized LazySingletonSynchronized getInstance() {
		if(instance == null) {
			instance = new LazySingletonSynchronized();
		}
		return instance;
	}
}
  • Or use enumeration
  • Single mode: lock-free implementation
public class LazySingleton {
	// 通过private修饰,就是为了防止这个类被随意创建
	private LazySingleton(){
		System.out.println("LazySingleton is created!!");
	}
	// 利用虚拟机的类初始化机制创建单例
	private static class SingletonHolder {
		private static LazySingleton instance = new LazySingleton();
	}
	// 为了防止对象被多次创建,使用synchronized关键字进行同步(缺点是并发环境下加锁,竞争激励的场合可能对性能产生一定影响)
	public static LazySingleton getInstance() {
		return SingletonHolder.instance;
	}
}

4, how to reduce the lock of competition

  • Reducing lock granularity
    used when the lock, the lock is protected by a plurality of objects, when the plurality of independent objects actually changed, as a plurality of locks to protect these eleven objects. But if there are multiple business methods while holding locks to avoid deadlock
  • Narrow the scope of the lock
    to achieve Kuaijinkuaichu to lock hold, try to shorten the time held by the lock. Some of the lock-independent code out of the lock range, especially some time-consuming, potentially blocking operation
  • Avoid unnecessary lock: lock statement between two very simple, resulting in locking of time longer than the execution of these statements, this time should be lock coarsening - to expand the scope of the lock.
  • Lock segment, ConcurrrentHashMap lock is a typical segment.
  • Replace exclusive lock

In the case of business allowed:

  • Read-write locks,
  • Spin-CAS
  • Concurrent use of the container system

Guess you like

Origin blog.csdn.net/m0_37661458/article/details/90704299