一.同步代码块(同步锁)
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
作用:用来修饰创建标记,使得子线程中的这个状态信息 可以在主线程及时得到修改