第18章 多线程----线程同步

Java提供了线程同步的机制来防止资源访问的冲突。

1、线程安全

实际开发中,使用多线程程序的情况很多,如银行排号系统、火车站售票系统等。这种多线程的程序通常会发生问题。

以火车站售票系统为例,在代码中判断当前票数是否大于0,如果大于0则执行将该票出售给乘客功能,但当两个线程同时访问这段代码时(假如这时只剩下一张票),第一个线程将票售出,与此同时第二个线程也已经执行完成判断是否有票的操作,并得出结论票数大于0,于是它也执行售出操作,这样就会产生负数。所以在编写多线程程序时,应该考虑到线程安全问题。实质上线程安全问题来源于两个线程同时存取单一对象的数据。

例如:在项目中创建ThreadSafeTest类,该类实现了Runnable接口,主要实现模拟火车站售票系统的功能。

public class ThreadSafeTest implements Runnable {
    int num = 10; // 设置当前总票数

    public void run() {
        while (true) {
            if (num > 0) {
                try {
                    Thread.sleep(100);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("tickets" + num--);
            }
        }
    }

    public static void main(String[] args) {
        ThreadSafeTest t = new ThreadSafeTest(); // 实例化类对象
        Thread tA = new Thread(t); // 以该类对象分别实例化4个线程
        Thread tB = new Thread(t);
        Thread tC = new Thread(t);
        Thread tD = new Thread(t);
        tA.start(); // 分别启动线程
        tB.start();
        tC.start();
        tD.start();
    }
}

从运行结果中看出,最后售的票为负值,这样就出现了问题。这是由于同时创建了4个线程,这4个线程执行run()方法,在num变量为1时,线程1、线程2、线程3、线程4都对num变量有存储功能,当线程1执行run()方法时,还没来得及做递减操作,就指定它调用sleep()方法进入休眠状态,这时线程2、线程3和线程4都进入了run()方法,发现num变量依然大于0,但此时线程1休眠时间已到,将num变量递减,同时线程2、线程3、线程4也都对num变量进行递减操作,从而产生了赋值。

2、线程同步机制

基本上所有解决多线程资源冲突问题的方法都是采用给定时间允许一个线程访问共享资源,这时就需要给共享资源上一道锁。这就好比一个人上洗手间时,他进入洗手间后会将门锁上,出来时再将锁打开,然后其他人才可以进入。

(1)同步块

在Java中提供了同步机制,可以有效第防止资源冲突。同步机制使用synchronized关键字。将资源放在同步块中,这个同步块被称为临界区。

synchronized(Object){
}

通常将共享资源的操作放置在synchronized定义的区域内,这样当其他线程也获取到这个锁时,必须等待锁被释放时才能进入该区域。Object为任意一个对象,每个对象都存在一个标志位,并具有两个值,分别为0和1。一个线程运行到同步块时首先检查对象的标志位,如果为0状态,表明此同步块中存在其他线程在运行。这时该线程处于就绪状态,直到处于同步块中的线程执行完同步块中的代码为止。这时该对象的标识位被设置为1,该线程才能执行同步块中的代码,并将Object对象的标识位设置为0,防止其他线程执行同步块中的代码。

例如:在本实例中,创建类ThreadSafeTest.java,在该类中修改上述中run()方法,把对num操作的代码设置在同步块中。

public class ThreadSafeTest implements Runnable {
    int num = 10;

    public void run() {
        while (true) {
            synchronized ("") {
                if (num > 0) {
                    try {
                        Thread.sleep(1000);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    System.out.println("tickets" + --num);
                }
            }
        }
    }

    public static void main(String[] args) {
        ThreadSafeTest t = new ThreadSafeTest();
        Thread tA = new Thread(t);
        Thread tB = new Thread(t);
        Thread tC = new Thread(t);
        Thread tD = new Thread(t);
        tA.start();
        tB.start();
        tC.start();
        tD.start();
    }
}

(2)同步方法

同步方法就是在方法前面修饰synchronized关键字的方法,其语法如下:

synchronized  void f(){
}

当某个对象调用了同步方法时,该对象上的其他同步方法必须等待该同步方法执行完毕后才能被执行。

时间: 2024-08-10 06:25:59

第18章 多线程----线程同步的相关文章

第18章 多线程----线程的生命周期

线程具有生命周期,其中包含7中状态,分别为:出生状态.就绪状态.运行状态.等待状态.休眠状态.阻塞状态.死忙状态. 1.线程的休眠 例如:在项目中创建SleepMethodTest类,该类继承了JFrame类,实现在窗体中自动画线段的功能,并且为线段设置颜色,颜色是随机产生的. import java.awt.*; import java.util.*; import javax.swing.*; public class SleepMethodTest extends JFrame { pri

第18章 多线程----线程的优先级

每个线程都具有各自的优先级,线程的优先级可以表明在程序中该线程的重要性,如果有很多线程处于就绪状态,系统会根据优先级来决定使哪个线程进入运行状态.每个新产生的线程都继承了父线程的优先级. 例如:在项目中创建PriorityTest类,该类实现了Runnable接口.创建4个进度条,分别由4个线程来控制,并且为这4个线程设置不同的优先级. import java.awt.*; import javax.swing.*; public class PriorityTest extends JFram

多线程&线程同步

线程 程序执行过程中,并发执行的代码段. 线程之间可以共享内存. 线程安全 增加了同步处理,确保在同一时刻,只有一个线程执行同步代码. 保证线程安全的方法就是锁机制 java中的任何对象都可以作为锁对象 synchronized(lock){....} 代码块中的代码被确保同一时间只有一个线程才能执行 同步方法是用当前对象作为同步对象(this) public synchronized int getTicket(){...} synchronized关键字也能加在方法上 确保同一时间只有一个线

多线程——线程同步,死锁

线程同步: 为什么需要同步 ①   线程同步是为了防止多个线程访问一个数据对象时,对数据造成破坏. ②   线程的同步是保证多线程安全访问竞争资源的一种手段. 同步和锁 ①   Java中每一个对象都有一个内置锁. ②   当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this实例)有关的锁:当程序运行到synchronized同步代码块时,自动获得锁定对象的锁. ③   获得一个对象的锁也称为获取锁.锁定对象.在对象上锁定或在对象上同步.当程序运

VC++深入详解——16章:线程同步,事件对象

这章介绍另外:事件对象和关键代码段. 进程相关函数: CreateEvent函数: 第一个参数:安全属性,默认的安全属性为NULL 第二个参数:复位方式, 人工设置为TRUE,自动设置为FALSE, 当为人工设置时,等待事件的线程时,需要resetevent函数来设置其为无型号状态. 第三个参数:初始状态:TRUE为有信号状态,FALSE为无信号状态. 第四个参数:对象名称,NULL为匿名名称. 创建或打开一个命名或匿名的事件对象(也属于内核对象) 返回:返回的是事件对象的句柄. SetEven

Java多线程 线程同步

如果你正在写一个变量,它可能接下来将被另一个线程读取,或者正在读取一个上一次已经被另一个线程写过的变量,那么你需要使用同步,并且,读写线程都必须用相同的监视器锁同步.--Brain同步规则 synchronized 所有对象都自动含有单一的锁,当在调用一个对象的任意synchronized方法时,此对象将被加锁. 对于某个特定对象来说,所有的synchronized方法共享同一个锁.所以某个线程在访问对象的一个synchronized方法时,其他线程访问该对象的任何synchronized方法都

Java多线程——线程同步

在之前,已经学习到了线程的创建和状态控制,但是每个线程之间几乎都没有什么太大的联系.可是有的时候,可能存在多个线程多同一个数据进行操作,这样,可能就会引用各种奇怪的问题.现在就来学习多线程对数据访问的控制吧. 由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题.Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问.   一.多线程引起的数据访问安全问题 下面看一个经典的问题,银行取钱的问题: 1).你有一张银行卡,里面有50

JAVA多线程线程同步问题

线程同步 在多线程的编程环境下,可能看着没有问题的代码在运行几千上万或者更多次后,出现了一些看着很奇怪的问题,出现这样的问题的原因就是可能会有两个或者更多个线程进入了同一块业务处理代码中导致了判断失效.为了解决这个问题,JAVA引入了同步监视器来解决这个问题.同步监视器的通用方法就是同步代码块,也就是给一块代码加了同步锁. package cn.test.hf; import java.math.BigDecimal; /** * 模拟取钱操作 */public class RunnableTe

9 C++ Boost 多线程,线程同步

线程的创建 boost_thread,boost_system 多线程的创建 线程的参数传递 线程的创建方式 线程的join 加入join,回收线程 线程中断 线程中断2, 线程组 boost 线程的死锁 boost 线程递归锁 线程互斥锁,线程同步 unique_lock 锁,离开作用域自动释放 unique_lock 锁 示例 2,可以显式的释放锁 boost 1次初始化 boost 条件变量 boost 线程锁,一个账户往另外一个账户转钱案例 boost upgrade_lock 知识背景