一.同步代码块(同步锁)
synchronized(锁){
操作功效数据的代码
}
同步代码块规则:
当线程进入代码块时,先看一下有没有锁.
如果有锁,就进入执行代码,进入的同时获得这把锁
执行完毕,出同步代码块,把锁放回去
如果没有锁,在同步代码块前等待
优点:数据安全
坏处:效率会降低
二.同步方法
使用关键字synchronized关键词声明在方法上
作用:同一时间,只有一个线程进入同步方法中执行代码.
//接口实现 class TicketRunnable1 implements Runnable{ private int ticket = 50; @Override public void run() { while(true) { if (sellTickets()) { break; } Thread.yield(); } }
public synchronized boolean sellTickets() { if (ticket>0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+ "****" +ticket); ticket--; //返回值 用作循环判断停止 return false; }else { return true; } }
三.Lock类
方法:使用实现类ReentrantLock创建一个对象,获取锁lock();释放锁unlock();
使用格式:
获取锁
try{
操作共享数据的代码
}finally{
释放锁
}
四.死锁
死锁是如何产生的:
线程t1先获取锁A,在同步锁的嵌套里等待锁B,同时线程t2先获取锁B,在同步锁里等待锁A;但是锁A被t1拥有,t1需要获得锁B才能释放锁A,同样锁B只有当t2获得锁A才能释放,故都在等待对方释放,形成了无限循环等待
死锁前提
1.必须要有同步锁的嵌套
2.锁唯一
死锁测试代码
//创建A锁 class LockA{ //为了唯一 private LockA() { } //定义一个常量 public static final LockA LOCK_A = new LockA(); } //创建B锁 class LockB{ //为了唯一 private LockB() { } //定义一个常量 public static final LockB LOCK_B = new LockB(); } class DeadlockRunnable implements Runnable{ //声明一个标记 private boolean isFlag = true; //第一次 先进A锁再进B锁 //下一次 先进B再进A @Override public void run() { //死循环 为了增加死锁的几率 while(true) { if (isFlag) { //A-B synchronized (LockA.LOCK_A) { System.out.println("我是if的LOCK_A锁"); synchronized (LockB.LOCK_B) { System.out.println("我是if的LOCK_B锁"); } } }else { //B-A synchronized (LockB.LOCK_B) { System.out.println("我是else的LOCK_B锁"); synchronized (LockA.LOCK_A) { System.out.println("我是else的LOCK_A锁"); } } } //修改标记 isFlag = !isFlag; } }
五.线程如何停止
stop()方法已经过时,不推荐使用使用标记法
先声明一个boolean类型的作为标记
public boolean ifFlag = false;
然这个标记成为接口中重写run方法的判断条件
while(!ifFlag) {
}
在主函数中,更该标记来中断线程
waitRunnable.ifFlag =true;
六.interrupt方法
1.调用interrupt方法时,线程中有wait() sleep() 等方法会抛出 InterruptException异常 并且清除中断状态
2.调用interrupt方法时,没有上述方法,
这时会设置(改变)中断状态的值(true--false)
七.关键字 volatile
作用:用来修饰创建标记,使得子线程中的这个状态信息 可以在主线程及时得到修改