多线程与高并发(一)

多线程与高并发

基础概念

创建一个线程的两种方式:

  1. 继承Thread
    class MyThread extends Thread{
    }
    new MyThread().start();
    
  2. 实现Runnable接口
    class MyThread implements Runnable {
    }
    new Thread(new MyRun()).start();
    

面试时候有时候会问到,启动线程的三种方式

  1. Thread、
  2. Runnable
  3. 通过Lambda表达式或者Executors.newCachedThread(线程池) 第三种说白了也是用的第一和第二的两种的其中一种

暂停线程的几种方式

  1. sleep,当前线程暂停一段时间,让给别的线程
  2. Yield,非常谦让的退出一下,让出一下cpu,先从cpu上运行先离开,进入到一个等待队列中,至于cpu从队列中拿出哪个,这个就是随机的了
  3. join,把其他线程加入到我运行的线程中,相当于两个线程t1,t2,当在t1中调用了t2.join的时候,t2执行完毕,t1才能继续执行,这块有个面试题:怎么能够保证三个线程按照顺序执行? 使用join方法就可以保证按照想要的顺序执行。

工作中,我们在多线程中,不建议使用stop()方法,该方法已经被废止,中断线程我们使用interrupt方法,打断,但是中止完方法,必须catch出exception,做相应的操作。一般很少用这几个方法。

获取一个线程的状态:使用getState()方法

synchronized 关键字
是一种同步锁。它修饰的对象有以下几种:

  1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
  2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
  3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
  4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
    synchronized 关键字加了以后,不加锁的效率是加锁的100倍,所以能不加锁尽量不要加锁
public class T {
	
	private int count = 10;
	private Object o = new Object();
	
	public void m() {
		synchronized(o) { //任何线程要执行下面的代码,必须先拿到o的锁
			count--;
			System.out.println(Thread.currentThread().getName() + " count = " + count);
		}
	}
}

锁定当前对象

public class T {
	
	private int count = 10;
	
	public void m() {
		synchronized(this) { //任何线程要执行下面的代码,必须先拿到this的锁
			count--;
			System.out.println(Thread.currentThread().getName() + " count = " + count);
		}
	}	
}
//或者这样写,这两种方法是等值的
public class T {

	private int count = 10;
	
	public synchronized void m() { //等同于在方法的代码执行时要synchronized(this)
		count--;
		System.out.println(Thread.currentThread().getName() + " count = " + count);
	}


}

当一个方法是静态方法的时候,static修饰的时候,synchronized修饰的话,修饰该方法,相当于给该类加锁即syn(T.class)

public class T {

	private static int count = 10;
	
	public synchronized static void a() { //这里等同于synchronized(FineCoarseLock.class)
		count--;
		System.out.println(Thread.currentThread().getName() + " count = " + count);
	}
	
	public static void n() {
		synchronized(T.class) { 
			count --;
		}
	}
}

synchronized是可重入锁,一个同步方法可以调用另外一个同步方法,一个线程已经拥有某个对象的锁,再次申请的时候仍然会得到该对象的锁

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();
 }
}

synchronized的底层实现
JDK早期的时候,实现是重量级 --》 OS操作系统申请锁
后来改进:
锁升级概念:可以查下 我就是厕所所长(一,二)
锁升级文章一
锁升级文章二
当我们使用sync(object) 上来后,先在Object头上面,markword记录这个线程ID,并没有加锁,这个叫偏向锁,如果有线程争用的话,偏向锁会升级为自旋锁,就是相当于另外一个线程一直在while(true)等待该线程结束,不会加入到cpu队列中去,自旋锁默认情况下旋转10次,若10次后,则升级为重量级锁—》OS操作系统锁(原子类)

那么有个问题:什么时候用自旋锁,什么时候用重量级锁?
执行时间比较短(加锁代码),线程数少,用自旋锁,
执行时间比较长,线程数多,用系统锁

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值