多线程与高并发--《线程基本概念》

一、基本概念

  • 进程:硬盘有一个程序叫QQ.exe,这是一个程序,这个程序是一个静态概念,双击启动,登录进去,这叫一个进程。进程相对于程序来说是一个动态的概念。
  • 线程:作为一个进程里面最小的执行单元他就叫线程,一个程序里不同的执行路径就叫一个线程。

二、创建线程几种方式

  1. new Thread().start();
  2. new Thread(Runnable).start();
  3. Executors.newCachedThreadPool();(或者FutureTask+Callable)

三、线程的几种状态

  • new:新建状态
  • Runnable:就绪状态(start后)
    • Ready:就绪状态
    • Running:运行状态
  • Terminated:结束状态
  • TimedWaiting:等待状态
  • Waiting:等待状态
  • Blocked:阻塞状态(没得到锁阻塞,得到锁就绪)
    ps:getState()方法可以获取线程状态在这里插入图片描述

四、线程的几个方法
sleep:当前线程暂停一段时间让给别的线程运行。到达了规定睡眠时间,自动复活。就绪状态
yield:当前线程正在执行的时候停下来进入等待队列,让出CPU,等待系统调度,依然有可能调度到它继续执行。就绪状态
join:两个线程t1,t2,当线程t1中执行了t2.join();t1跑去t2执行,即t1等待t2执行完毕再运行(自己join自己是没有任何意义的)
五、synchronized加锁方式

  • 加锁方式
    • 类锁一
       public void add(int m){
      	 synchronized (Account.class){
              String name = Thread.currentThread().getName();
              System.out.println("类锁添加" + m + "钱," + name + "添加后:" + (count+=m));
      	 }
      }
      
    • 类锁二
      public static synchronized void add(int m){
          String name = Thread.currentThread().getName();
          System.out.println("类锁添加" + m + "钱," + name + "添加后:" + (count+=m)); 
      }
      
    • 对象锁一
       public void add(int m){
              synchronized(this){
                  String name = Thread.currentThread().getName();
                  System.out.println("对象锁添加" + m + "钱," + name + "添加后:" + (count+=m));
              }
          }
      
    • 对象锁二
      public synchronized void add(int m){
              String name = Thread.currentThread().getName();
              System.out.println("对象锁添加" + m + "钱," + name + "添加后:" + (count+=m));
      }
      

总结

1.类锁是对静态方法使用synchronized关键字后,无论是多线程访问单个对象还是多个对象的sychronized块,都是同步的。

2.对象锁是实例方法使用synchronized关键字后,如果是多个线程访问同个对象的sychronized块,才是同步的,但是访问不同对象的话就是不同步的。

3.类锁和对象锁是两种不同的锁,可以同时使用,但是注意类锁不要嵌套使用,这样子容易发生死锁。

六、synchronized可重入概念

  • 重入锁概念:拿到了这把锁后不停地加锁,加了很多道,但锁定的对象还是同一个,去掉一道减一。
  • synchronized的一个属性:可重入
    • 如果一个同步方法调用另一个同步方法,有一个方法加了锁,另一个方法也需要加锁,加的是同一把锁也是同一个线程,那这个时候申请仍然会得到该对象的锁。

      /**
       * 一个同步方法可以调用另外一个同步方法,一个线程已经拥有某个对象的锁,再次申请的时候仍然会得到该对象的锁.
       * 也就是说synchronized获得的锁是可重入的
       * @author mashibing
       */	
      import java.util.concurrent.TimeUnit;
      
      public class T {
      	synchronized void m1() {
      		System.out.println("m1 start");
      		try {
      			TimeUnit.SECONDS.sleep(1);
      		} catch (InterruptedException e) {
      			e.printStackTrace();
      		}
      		m2();
      		System.out.println("m1 end");
      	}
      	
      	synchronized void m2() {
      		try {
      			TimeUnit.SECONDS.sleep(2);
      		} catch (InterruptedException e) {
      			e.printStackTrace();
      		}
      		System.out.println("m2");
      	}
      
      	public static void main(String[] args) {
      		new T().m1();
      	}
      }
      

七、异常锁

  • 程序在执行过程中,如果出现异常,默认情况锁会被释放,所以,在并发处理的过程中,有异常要多加小心,不然可能会发生不一致的情况。比如,在一个web app处理过程中,多个servlet线程共同访问同一个资源,这时如果异常处理不合适,在第一个线程中抛出异常,其他线程就会进入同步代码区,有可能会访问到异常产生时的数据。因此要非常小心的处理同步业务逻辑中的异常。
    import java.util.concurrent.TimeUnit;
    public class T {
    	int count = 0;
    	synchronized void m() {
    		System.out.println(Thread.currentThread().getName() + " start");
    		while(true) {
    			count ++;
    			System.out.println(Thread.currentThread().getName() + " count = " + count);
    			try {
    				TimeUnit.SECONDS.sleep(1);
    				
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			
    			if(count == 5) {
    				int i = 1/0; //此处抛出异常,锁将被释放,要想不被释放,可以在这里进行catch,然后让循环继续
    				System.out.println(i);
    			}
    		}
    	}
    	public static void main(String[] args) {
    		T t = new T();
    		Runnable r = new Runnable() {
    
    			@Override
    			public void run() {
    				t.m();
    			}
    		};
    		new Thread(r, "t1").start();
    		try {
    			TimeUnit.SECONDS.sleep(3);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		new Thread(r, "t2").start();
    	}
    }
    

八、synchronized底层实现(锁升级过程)

  • 早期jdk,它是重量级的,就是synchronized要去OS申请锁,效率很低
  • 改进后,锁升级(原本要去OS申请锁,现在当我们使用时,HosPot实现方式如下:synchronized(Object)加锁后,来了之后再Object头上MarkWord记录这个线程,如果第一个线程访问时候实际上是没有给Object加锁的,内部实现只是记录这个线程ID(偏向锁))
  • 偏向锁如果有线程争用,就升级自旋锁(自身旋转占用CPU,默认10次)
  • 自旋10次后升级为重量锁,即去OS申请资源。

关于自旋锁和重量级锁的选择:1. 加锁的代码执行时间段,线程数量少用自旋。2. 执行时间长线程数多,用系统(OS)锁

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值