Java多线程再学习,温故知新(二)多线程带来的风险

线程安全性问题

1.多线程环境下

2.多个线程共享一个资源

3.对资源进行非操原子性作

public class Sequence {
	
	private int value;
	
	/**
	 * synchronized 放在普通方法上,内置锁就是当前类的实例
	 * @return
	 */
	public synchronized int getNext() {
		return value ++;
	}
	
	/**
	 * 修饰静态方法,内置锁是当前的Class字节码对象
	 * Sequence.class
	 * @return
	 */
	public static synchronized int getPrevious() {
//		return value --;
		return 0;
	}
	
	public int xx () {
		
		// monitorenter
		synchronized (Sequence.class) {
			
			if(value > 0) {
				return value;
			} else {
				return -1;
			}
			
		}
		// monitorexit
		
	}
	
	public static void main(String[] args) {
		
		Sequence s = new Sequence();
//		while(true) {
//			System.out.println(s.getNext());
//		}
		
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				while(true) {
					System.out.println(Thread.currentThread().getName() + " " + s.getNext());
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
		
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				while(true) {
					System.out.println(Thread.currentThread().getName() + " " + s.getNext());
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
		
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				while(true) {
					System.out.println(Thread.currentThread().getName() + " " + s.getNext());
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
		
	}

}

通过命令查看java字节码,可以看到value 是一个共享变量,value++是两步操作[iadd,putfield],这是一个非原子操作因此如果不加synchronized在多线程条件下会发生线程安全性问题。

javap -verbose Seqence.class

活跃性问题

1.死锁

       同时获取共享资源,又同时不释放资源就会死锁

2.饥饿与公平

      低优先级的线程没有机会获取资源就会出现饥饿

      高优先级吞噬所有低优先级的CPU时间片
      线程被永久堵塞在一个等待进入同步块的状态
      等待的线程永远不被唤醒

      如何尽量避免饥饿问题
      设置合理的优先级
      使用锁来代替synchronized

3.活锁

       同时获取资源,同时互相让对方,导致无法获取资源

性能问题

     1.多线程序程在核单服务器上性能不一并定快

     2.时间片切换非常占用CPU资源

     3.线程间的上下文切花也非常消耗CPU资源

线程安全性问题的解决

1.Synchronized原理与使用

      原理:
      内置锁(Java中每一个对象都可以用作同步的锁,这些锁就可以被称之为内部锁),锁是互斥的,当代码进入到Synchronized代码块时,先拿到锁的线程可以进入执行状态,没有拿到锁的进入等待。

      从字节码的角度来看进入同步代码块会执行monitorenter,执行完毕你会执行monitorexit,同步方法是通过另一种方式实现ACC_SYNCHRONIZED。

      java的虚拟机中的锁的信息保存在对象的对象头中。

              Mark Word 存放对象的Hash值,锁信息【线程id,Epoch,对象的分代年龄信息,是否是偏向锁,锁标志位】等
              Class Metadata Address 对象类型地址等
              Array Length 如果是数组会保存数组长度

      用法:
      修饰普通方法,synchronized 放在普通方法上,内置锁就是当前类的实例(this对象)
      修饰静态方法,修饰静态方法,内置锁是当前的Class字节码对象
      修饰代码块,可以锁任意对象

2.Synchronized锁的类型

     偏向锁,等到竞争出现才会释放

             每次获取锁和释放锁会浪费资源,
             很多情况下,竞争锁不是由多个线程,而是由一个线程在使用。
             试用场景:只有一个线程在访问同步代码块的场景,性能会比较高


     轻量级锁

             允许多个线程可以同时进入同步代码块,多个线程会Mark Word查看锁类型,当第一个线程获得锁以后第二个线程会自动升级成重量级锁。
             线程在获取资源时,如果获取不到会通过自旋的方式【消耗cpu资源相当于while(true)】等待获取。


     重量级锁

3.自旋锁,死锁和锁重入

      a.自旋锁 :当一个线程拿到对象头信息进入栈中时,有其他线程进入后会一直自旋等待其他线程执行完毕。

import java.util.Random;

/**
 * 多个线程执行完毕之后,打印一句话,结束
 */
public class Demo {
	
	public static void main(String[] args) {
		
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName() + " 线程执行...");
				
				try {
					Thread.sleep(new Random().nextInt(2000));
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				
				System.out.println(Thread.currentThread().getName() + " 线程执行完毕了...");
			}
		}).start();
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName() + " 线程执行...");
				
				try {
					Thread.sleep(new Random().nextInt(2000));
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				
				System.out.println(Thread.currentThread().getName() + " 线程执行完毕了...");
			}
		}).start();
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName() + " 线程执行...");
				
				try {
					Thread.sleep(new Random().nextInt(2000));
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				
				System.out.println(Thread.currentThread().getName() + " 线程执行完毕了...");
			}
		}).start();

		
		while(Thread.activeCount() != 1) {
			// 自旋
		}
		System.out.println("所有的线程执行完毕了...");
	}

}

      b.锁重入:同一线程可以进入锁住同一个对象的多个代码块。

      执行结果a , b会很快被打印出来证明锁可以重入

public class Demo {
	
	public synchronized void a () {
		System.out.println("a");
		b();
	}
	
	public synchronized void b() {
		System.out.println("b");
	}
	//执行结果a , b会很快被打印出来证明锁可以重入
	public static void main(String[] args) {
		new Thread(new Runnable() {
			@Override
			public void run() {
                         Demo d = new Demo();
				d.a();
			}
		}).start();
	}
}

    不同的线程拿同一个对象加锁先调用a(),再调用b();

    再a()没有执行完的时候调用b()会被锁住,需要等a()执行完毕。

public class Demo {
	
	public synchronized void a () {
		System.out.println("a");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public synchronized void b() {
		System.out.println("b");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		Demo d1= new Demo();
		
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				d1.a();
			}
		}).start();
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				d1.b();
			}
		}).start();
	}
}

   不同的线程拿不同对象加锁先调用a(),再调用b();

    再a()没有执行完的时候调用b()不会被锁住,a,b很快会被打印出来,所有加锁应该用同一对象来调用。

public class Demo {
	
	public synchronized void a () {
		System.out.println("a");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public synchronized void b() {
		System.out.println("b");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		Demo d1= new Demo();
		Demo d2= new Demo();
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				d1.a();
			}
		}).start();
		new Thread(new Runnable() {
			@Override
			public void run() {
				d2.b();
			}
		}).start();
	}
}

   c.死锁:线程之间彼此拿着对方需要的资源,等待对方释放资源

public class Demo {
	
	private Object obj1 = new Object();
	private Object obj2 = new Object();
	
	public void a () {
		synchronized (obj1) {
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			synchronized (obj2) {
				System.out.println("a");
			}
		}
	}
	
	public void b () {
		synchronized (obj2) {
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			synchronized (obj1) {
				System.out.println("b");
			}
		}
	}
	
	public static void main(String[] args) {
		
		Demo d = new Demo();
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				d.a();
			}
		}).start();
		new Thread(new Runnable() {
			@Override
			public void run() {
				d.b();
			}
		}).start();
	}
}

猜你喜欢

转载自blog.csdn.net/ieflex/article/details/86309163