目录

  • Java开发入门
    • ● Java 概述
    • ● JDK的使用
    • ● 系统环境变量
    • ● 第一个Java程序
    • ● Java的运行机制
    • ● 教学设计
    • ● 课程讲义
    • ● 案例学习
    • ● 课后题答案
  • Java 编程基础
    • ● Java的基本语法
      • ● Java代码基本格式
      • ● Java中的注释
      • ● Java中的关键字
      • ● Java中的标识符
    • ● Java中的变量与常量
      • ● 变量的定义
      • ● 变量的数据类型
        • ● 案例导学
      • ● 变量的类型转换
        • ● 案例导学-自动类型转换
        • ● 案例导学-强制类型转换
      • ● 变量的作用域
        • ● 案例导学-作用域
      • ● Java中的常量
    • ● Java中的运算符
      • ● 案例导学-算术运算符
      • ● 案例导学-赋值运算符
      • ● 案例导学-比较运算符
      • ● 案例导学-逻辑运算符
      • ● 案例导学-位运算符
      • ● 案例导学-运算符的优先级
    • ● 选择结构语句
      • ● 案例导学-if条件语句
      • ● 案例导学-switch条件语句
    • ● 循环结构语句
      • ● 案例导学-while循环语句
      • ● 案例导学-for循环语句
      • ● 案例导学-循环嵌套
      • ● 案例导学-break语句
      • ● 案例导学-continue语句
    • ● 数组
      • ● 案例导学-一维数组的定义
      • ● 案例导学-数组最值
      • ● 案例导学-数组排序
    • ● 教学设计
    • ● 课程讲义
    • ● 课后题答案
  • 面向对象(上)
    • ● 面向对象概述
    • ● Java中的类与对象
      • ● 案例导学-类与对象
    • ● 类的封装
    • ● 案例导学-类的封装
    • ● 方法的重载和递归
    • ● 构造方法
      • ● 案例导学-构造方法与重载
    • ● this关键字
      • ● 案例导学-this
    • ● static关键字
      • ● 案例导学-静态变量
      • ● 案例导学-静态方法
      • ● 案例导学-静态代码块
    • ● 教学设计
    • ● 课程讲义
    • ● 章节测试
    • ● 课后题答案
  • 面向对象(下)
    • ● 类的继承
      • ● 案例导学-类的继承
      • ● 案例导学-方法的重写
      • ● 案例导学-super访问父类成员变量
      • ● 案例导学-super访问父类成员方法
      • ● 案例导学-super访问父类构造方法
    • ● final关键字
    • ● 抽象类和接口
    • ● 多态
    • ● 内部类
    • ● JDK8的Lambda表达式
    • ● 异常
    • ● 垃圾回收
    • ● 教学设计
    • ● 课程讲义
    • ● 章节测试
    • ● 课后习题答案
  • Java中的常用类
    • ● String类和StringBuffer类
    • ● System类与Runtime类
    • ● Math类与Random类
    • ● 包装类
    • ● 日期与时间类
    • ● 格式化类
    • ● 课后题答案
    • ● 课程讲义
  • 集合
    • ● 集和概述
    • ● Collection接口
    • ● List接口
      • ● List接口简介
      • ● ArrayList集合
      • ● LinkList集合
    • ● Collection集合遍历
    • ● Set接口
    • ● Map接口
    • ● 泛型
    • ● 常用工具类
      • ● Collections工具类
      • ● Arrays工具类
    • ● 课后题参考答案
    • ● 课程讲义
  • IO流
    • ● I/O流概述
    • ● 字节流
    • ● 字符流
    • ● File类
    • ● RandomAccessFile
    • ● 对象序列化
    • ● NIO
    • ● NIO.2
    • ● 课后题答案
    • ● 课程讲义
    • ● 章节测试
  • GUI(图形用户接口)
    • ● Swing概述
    • ● Swing顶级容器
    • ● 布局管理器
    • ● 事件处理
    • ● Swing常用组件
    • ● Swing组件的整合使用
    • ● JavaFX图形用户界面工具
    • ● 课程讲义
  • JDBC
    • ● 什么是JDBC
    • ● JDBC常用API
    • ● JDBC编程
    • ● 案例-使用JDBC实现QQ登录
    • ● 课程讲义
  • 多线程
    • ● 线程概述
    • ● 线程的创建
    • ● 线程的生命周期及状态转换
    • ● 线程的调度
    • ● 多线程同步
    • ● 多线程通信
    • ● 教学设计
    • ● 课后题参考答案
    • ● 课程讲义
  • 网络编程
    • ● 网络通信协议
    • ● UDP通信
    • ● TCP通信
    • ● 课程讲义
  • Eclipse开发工具
    • ● Eclipse概述
    • ● Eclipse的安装与启动
    • ● Eclipse进行程序开发
    • ● Eclipse程序调试
    • ● 使用Eclipse导出、导入jar文件
  • ACM大赛题库
    • ● 2027
    • ● 2028
    • ● 2024-2026
    • ● 2012-2023
    • ● 2018-2020
    • ● 2015-2017
    • ● 2012-2014
    • ● 2009-2011
    • ● 2003-2005
    • ● 2000-2002
  • PBL学生风采展示
    • ● 实验一
    • ● 实验二
    • ● 实验三
    • ● 实验四
多线程同步

一、多线程的安全问题

  1. 为什么会出现安全问题?

  2. 因为程序在运行时,会出现一个线程在判断条件满足后,具备了执行资格,但没有运行代码

  3. 后一个线程也判断了条件,也具备了执行资格,后一个线程运行了代码,但这时候,线程运行的条件不满足了

  4. 这时候,就出现了安全问题


  5. 2.问题的原因?

  6. 当多条语句在操作同一线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完

  7. 另一个线程参与进来,导致
  8. 共享数据错误
  9.  

  10. 解决办法

  11.   对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其它线程不参与执行。


二、同步代码块    

    即有synchronized关键字修饰的语句块。    
    被该关键字修饰的语句块会自动被加上内置锁,从而实现同步

代码如:    
    synchronized(object){    
    }

    注:同步是一种高开销的操作,因此应该尽量减少同步的内容。    
    通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。

    /**

     * 线程同步的运用

     * 

     * @author XIEHEJUN

     * 

     */

    public class SynchronizedThread {

        class Bank {

            private int account = 100;

            public int getAccount() {

                return account;

            }

            /**

             * 用同步方法实现

             * 

             * @param money

             */

            public synchronized void save(int money) {

                account += money;

            }

            /**

             * 用同步代码块实现

             * 

             * @param money

             */

            public void save1(int money) {

                synchronized (this) {

                    account += money;

                }

            }

        }

        class NewThread implements Runnable {

            private Bank bank;


            public NewThread(Bank bank) {

                this.bank = bank;

            }

            @Override

            public void run() {

                for (int i = 0; i < 10; i++) {

                    // bank.save1(10);

                    bank.save(10);

                    System.out.println(i + "账户余额为:" + bank.getAccount());

                }

            }


        }

        /**

         * 建立线程,调用内部类

         */

        public void useThread() {

            Bank bank = new Bank();

            NewThread new_thread = new NewThread(bank);

            System.out.println("线程1");

            Thread thread1 = new Thread(new_thread);

            thread1.start();

            System.out.println("线程2");

            Thread thread2 = new Thread(new_thread);

            thread2.start();

        }

        public static void main(String[] args) {

            SynchronizedThread st = new SynchronizedThread();

            st.useThread();

        }

    }

三、同步方法    
    即有synchronized关键字修饰的方法。    
    由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,    
    内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。

    代码如:    
    public synchronized void save(){}

   注: synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类

四、死锁问题

1、什么是死锁:

在编写多线程的时候,必须要注意资源的使用问题,如果两个或多个线程分别拥有不同的资源,
而同时又需要对方释放资源才能继续运行时,就会发生死锁。

简单来说:死锁就是当一个或多个进程都在等待系统资源,而资源本身又被占用时,所产生的一种状态。

2、造成死锁的原因:    
多个线程竞争共享资源,由于资源被占用,资源不足或进程推进顺序不当等原因造成线程处于永久阻塞状态,从而引发死锁

3、当然死锁的产生是必须要满足一些特定条件的:

(1)互斥条件:进程对于所分配到的资源具有排它性,即一个资源只能被一个进程占用,直到被该进程释放;  

(2)请求和保持条件:一个进程因请求被占用资源而发生阻塞时,对已获得的资源保持不放。    
    (3)不剥夺条件:任何一个资源在没被该进程释放之前,任何其他进程都无法对他剥夺占用    

(4)循环等待条件:当发生死锁时,所等待的进程必定会形成一个环路(类似于死循环),造成永久阻塞。

/**

 * 用两个线程请求被对方占用的资源,实现线程死锁 

 */

public class DeadLockThread implements Runnable {

    private static final Object objectA = new Object();

    private static final Object objectB = new Object();

    private boolean flag;

    @Override

    public void run() {

        String threadName = Thread.currentThread().getName();

        System.out.println("当前线程 为:" + threadName + "\tflag = " + flag);

        if (flag) {

            synchronized (objectA) {

                try {

                    Thread.sleep(1000);

                } catch (InterruptedException e) {

                    // TODO Auto-generated catch block

                    e.printStackTrace();

                }

                System.out.println(threadName + "已进入同步代码块objectA,准备进入objectB");

                synchronized (objectB) {

                    System.out.println(threadName + "已经进入同步代码块objectB");

                }

            }

        } else {

            synchronized (objectB) {

                try {

                    Thread.sleep(1000);

                } catch (InterruptedException e) {

                    // TODO Auto-generated catch block

                    e.printStackTrace();

                }

                System.out.println(threadName + "已进入同步代码块objectB,准备进入objectA");

                synchronized (objectA) {

                    System.out.println(threadName + "已经进入同步代码块objectA");

                }

            }

        }

    }

    public static void main(String[] args) {

        DeadLockThread deadlock1 = new DeadLockThread();

        DeadLockThread deadlock2 = new DeadLockThread();

        deadlock1.flag = true;

        deadlock2.flag = false;

        Thread thread1 = new Thread(deadlock1);

        Thread thread2 = new Thread(deadlock2);

        thread1.start();

        thread2.start();

    }

}