多线程
1.同步方法和非同步方法是否可以同时调用
可以同时调用,可以理解为不加synchronized的方法无视这个对象的锁
int count=10; public synchronized void m1(){ count--; System.out.println(Thread.currentThread().getName()+"m1 started 01 : "+count); try { Thread.sleep(10000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"m1 started 02 : "+count); } public /*synchronized*/ void m2(){ try { count--; Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"m2 start ..."+count); } public static void main(String[] args) { Test t = new Test(); new Thread(()->t.m1(),"m1").start(); new Thread(()->t.m2(),"m2").start(); }
2.对业务写方法上枷锁,业务读方法上不加锁,可能会产生脏读现象(读到在写的过程中还没有完成的数据)
String name; Double price; public synchronized void set(String name,Double price){ this.name=name; try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } this.price=price; } public Double get(){ return this.price; } public static void main(String[] args) { Test t = new Test(); new Thread(()->t.set("zhangsan",100.00),"m1").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(t.get()); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(t.get()); }
3.一个同步方法可以调用另外一个同步方法,
一个线程已经拥有了某个对象的锁,再次申请的时候仍然会得到该对象的锁,也就是说synchronized获得的锁是可以重入的,子类也可以调用父类的同步方法
4.程序在执行过程中,如果出现异常
程序执行过程中如果出现异常,默认情况锁会被释放
所以在并发处理的过程中,有异常要多加小心,不然可能会发生不一致的情况,若是不想释放这个锁则加上try()catch()
5.volatile
这个和JMM有关(Java Memory Model)Java内存模型
在JMM中有一个内存为主内存(我们所说的栈内存、堆内存都可以理解为主内存),每一个线程在执行的过程中都有一个自己的内存,存放自己变量的内存。它会从主内存中读取数据,把数据放进自己的缓冲区内,然后进行操作,然而在没操作完之前,它不会再回到主内存中取读取数据,因为本地已经缓存了数据,操作完之后再写回主内存中。若是读的这条数据加上volatile,这条数据发生变化之后,就会通知其他线程,之前的数据过期了,请重新来主内存中读取数据。
注:volatile不能替代synchronized,volatile只能保证可见性,synchronized技能保证可见性,还能保证原子性,synchronized性能比volatile低
volatile boolean running=true; void m(){ System.out.println("m start"); while (running){ } System.out.println("m end"); } public static void main(String[] args) { Test3 t = new Test3(); new Thread(()->t.m(),"m1").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } t.running=false; }
6.AtomicInteger
AtomicXXX类本身方法都是原子性的,但不能保证多个方法连续调用是原子性的。
7.细粒度的锁效率要比粗粒度的锁效率高
8.synchronized所对象
所是锁的是对象,则对的是堆内存中的对象,若是新new了一个对象,则这个对象已经不是原来的对象了,所以不能新new 这个对象。
9.不要以字符串常量作为锁的对象
10.wait、sleep、notify
wait释放锁,sleep不释放锁,notify也不释放锁。wait和notify都必须在synchronized同步代码块中使用,同样也是争夺的对象上的wait和notify
public class Test4 { List list = new ArrayList(); public void add(Object obj){ list.add(obj); } public int size(){ return list.size(); } public static void main(String[] args) { Test4 t = new Test4(); final Object obj = new Object(); new Thread(new Runnable() { @Override public void run() { synchronized (obj){ System.out.println("线程2启动"); if(t.size()!=5){ try { obj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("线程2结束"); obj.notify(); try { obj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); new Thread(new Runnable() { @Override public void run() { synchronized (obj){ System.out.println("线程1启动"); for (int i=0;i<10;i++){ System.out.println("add :"+i); t.add(obj); if (t.size()==5){ obj.notify(); try { obj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }).start(); } }
11.CountDownLatch
使用Latch(门闩)代替wait notify 来进行通知,好处是通信方式简单,同时也可以指定等待时间,使用await和countdown方法代替wait和notify,CountDownLatch不涉及锁定,当count的值为零时(每一次执行countdown方法时都会减一),当前线程继续执行。当不涉及同步,只是涉及线程通信的时候,用CountDownLatch。
public class Test4 { List list = new ArrayList(); public void add(Object obj){ list.add(obj); } public int size(){ return list.size(); } public static void main(String[] args) { Test4 t = new Test4(); CountDownLatch latch = new CountDownLatch(1); new Thread(new Runnable() { @Override public void run() { System.out.println("线程2启动"); if(t.size()!=5){ try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("线程2结束"); } }).start(); new Thread(new Runnable() { @Override public void run() { System.out.println("线程1启动"); for (int i=0;i<10;i++){ System.out.println("add :"+i); t.add(latch); if (t.size()==5){ latch.countDown(); } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } }
12.ReenTrantLock
使用reentrantlock同样可以锁住对象,需要注意的是reentrantlock必须要必须要必须要手动释放锁
使用syn锁定的话若果遇到异常,jvm会自动释放锁,但是lock必须手动释放锁,因此经常在finally中进行锁的释放
注:这个用起来比较灵活,他有一个方法为:lock.trylock(); 返回的是布尔类型的值,拿着锁会怎样执行,没有拿着锁另一种方式执行,这样可以进行判断,还可以给他指定一个特定的时间,lock.trylock(5,TimeUtil.SECCONDS),指定5秒后。ck.lockInterruptibly()也可以打断自己等锁,去执行其他的任务,打断方法为t2.interrupt();Lock lock = new ReentrantLock(true)这个是公平锁
public class Test6 { Lock lock = new ReentrantLock(); public void m1(){ lock.lock(); System.out.println("线程 m1 启动"); try { for (int i=0;i<5;i++){ System.out.println("do : "+i); TimeUnit.SECONDS.sleep(1); } } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } } public void m2(){ lock.lock(); System.out.println("线程 m2 启动"); lock.unlock(); } public static void main(String[] args) { Test6 t = new Test6(); new Thread(t::m1).start(); new Thread(t::m2).start(); }
/*public void m2(){
Boolean locked=false;
try {
locked = lock.tryLock(2,TimeUnit.SECONDS);
if (locked){
System.out.println("线程 m2 拿到锁 并 启动");
}else {
System.out.println("线程 m2没有拿到锁 启动");
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
if (locked)lock.unlock();
}
}*/
}
打断线程的等待
public class Test6 { Lock lock = new ReentrantLock(true); public void m1(){ lock.lock(); System.out.println("线程 m1 启动"); try { for (int i=0;i<5;i++){ System.out.println("do : "+i); SECONDS.sleep(1); } } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } } public void m2(){ try { // lock.lock(); System.out.println("线程2 等待获取锁"); //若是长时间拿不到锁,自己停止等待,继续执行其他程序 lock.lockInterruptibly(); // lock.tryLock(2, SECONDS); // SECONDS.sleep(2); System.out.println("正常执行"); } catch (InterruptedException e) { System.out.println("线程2不再等待锁,去执行其他任务"); }finally { try{ lock.unlock(); }catch (Exception e){ System.out.println("没有获取锁"); } } } public static void main(String[] args) { Test6 t = new Test6(); Thread thread1 = new Thread(new Runnable() { @Override public void run() { t.m1(); } }); thread1.start(); Thread thread2 = new Thread(new Runnable() { @Override public void run() { t.m2(); } }); thread2.start(); try { SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } thread2.interrupt(); } }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步