多线程的锁总结

同步代码块(同步锁)

写法

synchronized(锁){
    加锁的代码
} 

运行原理:当线程进入同步锁, 会把锁拿走, 执行代码块中的代码, 代码执行完毕后, 会把锁还回去; 如果线程遇到同步代码块, 发现没有锁, 将进入等待(有锁才能进去)

注意:
保证所有线程使用的都是同一把锁
锁可以使用任意一个对象(同一个对象就行)

下面是运用同步锁编写的一个卖门票例子

public class Demo01 {
    public static void main(String[] args) {
        // 利用接口的实现类 创建三个线程出来
        Tickets tickets = new Tickets();
        // 创建三个线程
        Thread t1 = new Thread(tickets,"窗口1");
        Thread t2 = new Thread(tickets,"窗口2");
        Thread t3 = new Thread(tickets,"窗口3");
        // 开启线程
        t1.start();
        t2.start();
        t3.start();
    }
}

// 利用接口方法 保证访问的共享资源
class Tickets implements Runnable {
    // 票总数
    private int tickets = 50;
    // 声明锁对象(保证锁也是线程共享的)
    private final Object obj = new Object();

    // 卖票
    @Override
    public void run() {
        // 利用循环 保证票都能卖出去
        while (true) {
            synchronized (obj) {
                // 休眠(可以放大问题)
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                // 判断票
                if (tickets > 0) {
                    // 可以卖
                    System.out.println(Thread.currentThread().getName() + "--" + tickets);
                    // 卖票
                    tickets--;
                } else {
                    // 卖完了
                    System.out.println("卖完了");
                    break;
                }
            }
            // 让线程让出CPU的执行资源(可能让出增加几率)
            Thread.yield();
        }
    }
}
运用同步方法来实现,使用synchronized关键词修饰方法
public class Demo02 {
    public static void main(String[] args) {
        // 利用接口的实现类 创建三个线程出来
        Tickets1 tickets = new Tickets1();
        // 创建三个线程
        Thread t1 = new Thread(tickets,"窗口1");
        Thread t2 = new Thread(tickets,"窗口2");
        Thread t3 = new Thread(tickets,"窗口3");
        // 开启线程
        t1.start();
        t2.start();
        t3.start();
    }
}

// 利用接口方法 保证访问的共享资源
class Tickets1 implements Runnable {
    // 票总数
    private static int tickets = 50;
    // 声明锁对象(保证锁也是线程共享的)
    private final Object obj = new Object();

    // 卖票
    @Override
    // 封装方法
    public void run() {
        // 利用循环 保证票都能卖出去
        while (true) {
            if (sellTickets()) {
                break;
            }
            // 让线程让出CPU的执行资源(可能让出 增加几率)
            Thread.yield();
        }
    }

    // 静态方法也可以使用锁,类锁,成员方法使用的对象锁是this
    public static synchronized boolean sellTickets() {
        if (tickets > 0) {
            System.out.println(Thread.currentThread().getName() + "--" + tickets);
            tickets--;
            return false;
        } else {
            // 卖完了
            System.out.println("卖完了");
            return true;
        }
    }
}
运用Lock接口实现

写法

lock();
try{
加锁的代码
} finaly{
unlock();
}
public class Demo03 {
    public static void main(String[] args) {
        // 利用接口的实现类 创建三个线程出来
        Tickets2 tickets = new Tickets2();
        // 创建三个线程
        Thread t1 = new Thread(tickets,"窗口1");
        Thread t2 = new Thread(tickets"窗口2");
        Thread t3 = new Thread(tickets"窗口3");
        // 开启线程
        t1.start();
        t2.start();
        t3.start();
    }
}

// 利用接口方法 来保证 访问的共享资源
class Tickets2 implements Runnable {
    // 票总数
    private int tickets = 50;
    // 声明锁对象(保证锁也是线程共享的)
    private final Object obj = new Object();
    // 声明lock锁
    // 参数:true 可以尽量让线程公平进入锁(不绝对)
    private final ReentrantLock lock = new ReentrantLock(true);

    // 卖票
    @Override
    public void run() {
        // 利用循环 保证票都能卖出去
        while (true) {
            // 使用lock锁
            lock.lock();
            try {
                if (tickets > 0) {
                    System.out.println(Thread.currentThread().getName() + "--" + tickets);
                    tickets--;
                } else {
                    break;
                }
            } finally {
                // 释放锁
                lock.unlock();
            }
            // 让线程让出CPU的执行资源(可能让出 增加几率)
            Thread.yield();
        }
    }
}

死锁(尽量避免)

出现前提:
1.至少两个线程
2.锁的嵌套(同步代码块的嵌套)
3.两把锁

public class Demo05 {
    public static void main(String[] args) {
        DieLockRunnable runnable = new DieLockRunnable();
        Thread t1 = new Thread(runnable);
        Thread t2 = new Thread(runnable);
        t1.start();
        t2.start();       
    }
}

// 声明锁
class LockA{
    // 私有化构造方法
    private LockA() {
        // TODO Auto-generated constructor stub
    }
    // 创建锁对象(声明个常量)
    public static final LockA A = new LockA();
}

class LockB{
    // 私有化构造方法
    private LockB() {
        // TODO Auto-generated constructor stub
    }
    // 创建锁对象(声明个常量)
    public static final LockB B = new LockB();
}

// 线程
class DieLockRunnable implements Runnable{
    // 利用标记来控制 先A->B 或 先B->A
    boolean isTrue = false;
    @Override
    public void run() {
        // 利用死循环 增加死锁几率
        while (true) {
            // 不断让两个线程            
            if (!isTrue) {
             // 先进A锁在进B锁(同步代码块嵌套)
                synchronized (LockA.A) {
                    System.out.println("我是if中的 A锁");
                    synchronized (LockB.B) {
                        System.out.println("我是if中的 B锁");
                    }
                }
            } else {
             // 下一次从B锁进A锁
                synchronized (LockB.B) {
                    System.out.println("我是else中的B锁");
                    synchronized (LockA.A) {
                        System.out.println("我是else中的A锁");
                    }
                }
            }
            // 改变一下标记
            isTrue = !isTrue;
        }
    }    
}

如何停止线程?

  • stop()方法, 已经过时, 不推荐用

  • interrupt()方法可以改变中断状态, 初值false --> true, 当线程中有sleep, wait, join方法时,会抛出异常InterruptException, 中断状态将被清除, 这时interrupt()的值还是false, 所以我不喜欢用

  • 推荐方式:直接使用标记停止线程,以下为使用例子

public class Demo06 {
    public static void main(String[] args) {
        InterruptRunnable runnable = new InterruptRunnable();
        Thread t = new Thread(runnable);
        t.start();
        // 休眠几秒 给子线程运行的时间
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // 利用标记停止线程
        runnable.isTrue = true;
        System.out.println("调用线程中断方法---");
        // 让主线程运行一会
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("主线程结束");
    }
}

class InterruptRunnable implements Runnable{
    // 声明标记 控制线程的停止
    public boolean isTrue = false;   
    @Override
    public void run() {
        while (!isTrue) {
            try {
                // 中断状态被清除指的是 
                // 从休眠状态-->运行状态(或者受阻塞)
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "run");
        }
    }    
} 

从子线程中修改状态, 主线程中是否能够立即接收到?

答:当你从子线程中修改状态时, 主线程不能立即接收到这个状态的改变, 使用关键词 volatile 来标识你改变的状态的变量 ,可以让主线程立即接收到改变的值
以下为实验案例

public class Demo08 {
    public static void main(String[] args) {
        ChangeRunnable runnable = new ChangeRunnable();
        Thread t = new Thread(runnable);
        t.start();
        
        // 利用线程中标记 卡住主线程
        while (!runnable.isTrue) {            
        }
        System.out.println("主线程结束");
    }
}

class ChangeRunnable implements Runnable{
    // 使用关键词 volatile 来标识你改变的状态的变量
    // 效果:可以让主线程立即接收到 改变的值  
    public volatile boolean isTrue = false;
    // 记录循环次数
    private int num = 0;
    @Override
    public void run() {
       while (!isTrue) {
        num++;
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        if (num == 5) {
            // 修改状态
            isTrue = true;
        }
        System.out.println(Thread.currentThread().getName() + "--" + num);
    }
    }
}

猜你喜欢

转载自blog.csdn.net/guxin0729/article/details/82778320
今日推荐