博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
多线程的同步锁和死锁(详细)
阅读量:4567 次
发布时间:2019-06-08

本文共 5601 字,大约阅读时间需要 18 分钟。

    最近发现,编程这东西,一段时间不用,就差不多忘了,感觉脑子永远不够用,这下利用点时间整理下思路,记录下来,已被不时之需。

         

          


线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程什么是多线程呢?即就是一个程序中有多个线程在同时执行。

 

单线程程序:即,若有多个任务只能依次执行。当上一个任务执行结束后,下一个任务开始执行。如,去网吧上网,网吧只能让一个人上网,当这个人下机后,下一个人才能上网。

多线程程序:即,若有多个任务可以同时执行。如,去网吧上网,网吧能够让多个人同时上网。

 

程序运行原理

 

分时调度

 

所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。

 

 抢占式调度

 

优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性)Java使用的为抢占式调度。

 

大部分操作系统都支持多进程并发运行,现在的操作系统几乎都支持同时运行多个程序。比如:现在我们上课一边使用编辑器,一边使用录屏软件,同时还开着画图板,dos窗口等软件。此时,这些程序是在同时运行,感觉这些软件好像在同一时刻运行着

 

实际上,CPU(中央处理器)使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核而言,某个时刻,只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻运行。

 

其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高

 

 线程安全

 

如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的

 

/** *  * @author LYJ *    实现Runnable的代码 *     */public class Ticket implements Runnable {    //设置总票数为100,这里的ticket是成员变量,    //由于在测试类中new了一次,所以值存在一个,被三个售票窗口共享    int ticket=100;    public void run() {        //模拟售票        while(true) {            //如果票数大于0,继续售票            if(ticket>0) {                //为了让线程安全问题效果明显些,加入线程定时休眠Thread.sleep()                try {                    Thread.sleep(100);                } catch (InterruptedException e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }                //Thread.currentThread()是线程获取当前线程对象的方法    getName()获取调用者的线程名                System.out.println(Thread.currentThread().getName()+"正在售票:"+ticket--);            }        }    }/** *  * 开启多线程的代码 * */public class ThreadDemo01 {    public static void main(String[] args) {        //创建Ticket的Runnable对象        Ticket ticket = new Ticket();        //创建线程3个对象模拟三个售票窗口,并把Runnable对象加入Thread和给Thread命名        new Thread(ticket,"窗口1").start();;        new Thread(ticket,"窗口2").start();;        new Thread(ticket,"窗口3").start();;    } ***************************************************************** 输出结果: 窗口3正在售票:3 窗口2正在售票:2 窗口1正在售票:1 窗口3正在售票:0 窗口2正在售票:-1             结果中出现了负数和0,这就是线程安全问题,要怎么解决呢?             加同步锁 synchronized(Object o){....}  o可以是任意对象 ****************************************************************************** 加入同步锁后的代码 public class Ticket implements Runnable {
    //设置总票数为100,这里的ticket是成员变量,     //由于在测试类中new了一次,所以值存在一个,被三个售票窗口共享     int ticket=100;     public void run() {
        //模拟售票         while(true) {
            //如果票数大于0,继续售票                          //加入同步锁             synchronized(this) {
            if(ticket>0) {
                //为了让线程安全问题效果明显些,加入线程定时休眠Thread.sleep()                 try {
                    Thread.sleep(100);                 } catch (InterruptedException e) {
                    // TODO Auto-generated catch block                     e.printStackTrace();                 }                 //Thread.currentThread()是线程获取当前线程对象的方法    getName()获取调用者的线程名                 System.out.println(Thread.currentThread().getName()+"正在售票:"+ticket--);                 }             }         }     } ********************************************** 运行几次,发现运行结果中没有出现负数和0

 

同步方法:在方法声明上加上synchronized

public synchronized void method(){

    可能会产生线程安全问题的代码

}  

同步方法中的锁对象是 this(即调用者对象

静态同步方法: 在方法声明上加上static synchronized

public static synchronized void method(){

可能会产生线程安全问题的代码

}

静态同步方法中的锁对象是 类名.class(因为在加载类文件的时候,静态同步方法由于是静态的也被加载进内存了,类名.class的加载优先级高于静态方法

同步代码块:在需要同步的代码外面包上一个synchronized

(Object o){

    可能会产生线程安全问题的代码

} 

同步代码块中的所对象可以是任意对象

 

死锁

 

同步锁使用的弊端:当线程任务中出现了多个同步(多个锁)时,如果同步中嵌套了其他的同步。这时容易引发一种现象:程序出现无限等待,这种现象我们称为死锁。这种情况能避免就避免掉。

 

synchronzied(A){

 

  synchronized(B){

 

         

 

  }

 

}

/** *  *创建锁对象 * */public class Lock {    //这里用private封装,为了不让外面随便造锁,限制只能有A,B锁个一把,这样容易出现死锁    //即A同学和B同学想相互串门,可是没人只有一把自己房间的钥匙,而且各自都不愿意先给,于是死锁    private Lock() {};    public static final Object lockA =new Object();    public static final Object lockB = new Object();    //这里使用static 为了让外界可以通过类名调用成员变量lockA和lockB    //因为外面无法创建Lock对象,为了让外面在不创对象的情况下调用,加了static,通过类名加变量名访问}/** * 线程任务类 *  */import java.util.Random;public class ThreadTask implements Runnable {    int x = new Random().nextInt(1);//用随机数随机获取0、1,来模拟CPU随机分配执行权的行为    @Override    public void run() {        while(true) {            if(x%2==0) {                //情况一//                先执行A再执行B:即A同学先拿了A门的钥匙去开A门,然后打算开B门                synchronized(Lock.lockA) {                    System.out.println("A同学...开A门");                    synchronized(Lock.lockB) {                        System.out.println("A同学...开B门");                    }                }            }else {                //情况二//                先执行B执行A:B同学先拿了B门的钥匙,去开B门,然后打算开A门                synchronized(Lock.lockB) {                    System.out.println("B同学...开B门");                    synchronized(Lock.lockA) {                        System.out.println("B同学...开A门");                    }                }            }            x++;        }    }/** *  * 线程测试类 * */public class ThreadDemo {    public static void main(String[] args) {        //创建Runnable的实现类对象        ThreadTask tt = new ThreadTask();        //把Runnable实现类对象加入线程中,创建2个线程        Thread t1 = new Thread(tt);        Thread t2 = new Thread(tt);        t1.start();        t2.start();            } ********************************************************* 输出结果:A同学...开A门       A同学...开B门       B同学...开B门       B同学...开A门       A同学...开A门       B同学...开B门 结论:A同学或者B同学,一个人先后拿走两把钥匙时,线程是正常运行的,一旦A拿了A锁进去A门的时候,CPU突然让B开始执行,让B拿了B锁进入B门,结果A需要B锁,B也需要A锁,两者又不能后退 于是死锁现象发生了。

 等待唤醒机制

 

线程之间的通信:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。通过一定的手段使各个线程能有效的利用资源。而这种手段即等待唤醒机制

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/lyjblogs/p/7888646.html

你可能感兴趣的文章
大数据等最核心的关键技术:32个算法
查看>>
Maven多模块项目搭建
查看>>
redis列表list
查看>>
雷林鹏分享: C# 简介
查看>>
实用类-<Math类常用>
查看>>
构建之法阅读笔记之四
查看>>
10.15习题2
查看>>
Windows Server 2008 R2 备份与恢复详细实例
查看>>
Ubuntu上kubeadm安装Kubernetes集群
查看>>
关于java学习中的一些易错点(基础篇)
查看>>
MFC的多国语言界面的实现
查看>>
四则运算个人项目 最终版
查看>>
java线程系列---java5中的线程池
查看>>
SQL表连接
查看>>
新秀系列C/C++经典问题(四)
查看>>
memset函数具体说明
查看>>
经常使用的android弹出对话框
查看>>
确保新站自身站点设计的合理性的六大注意点
查看>>
promise
查看>>
Go 网络编程笔记
查看>>