java-多线程 | 线程安全和线程同步

线程安全

线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。
线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。

概述

如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。
线程安全问题都是由全局变量静态变量引起的。
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。

如何保证线程安全:

1.把共享的变量数据标识为private
2.使用synchronized关键字同步方法或代码。

volidate关键字的使用

volatile是一个类型 修饰符 (type specifier)。它是被设计用来修饰被不同线程访问和修改的 变量 。如果没有volatile,基本上会导致这样的结果:要么无法编写多线程程序,要么 编译器 失去大量优化的机会。

Volidate 和Synchroinzed 区别

java中使用Volidate变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功能也仅是 synchronized 的一部分。Synchronized是对Volidate的基础上增加了互斥的功能。
1.Volidate:只保证可见性,可以多个线程同时访问voliadte修饰的变量。
2.Synchroinzed:既保证了可见性又保证了互斥性。同时只能有一个线程去访问。 volatile 变量可以被看作是一种 “程度较轻的 synchronized”;与 synchronized 块相比,volatile 与锁相比,Volatile 变量是一种非常简单但同时又非常脆弱的同步机制,它在某些情况下将提供优于锁的性能和伸缩性。如果严格遵循 volatile 的使用条件 —— 即变量真正独立于其他变量和自己以前的值 —— 在某些情况下可以使用 volatile 代替 synchronized 来简化代码。然而,使用 volatile 的代码往往比使用锁的代码更加容易出错。
当使用volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。

经典卖票的例子

public class Multithread implements Runnable
{

    //总票数
    private int ticket = 100;

    @Override
    public void run()
    {
        saleTicket();
        try
        {
            Thread.sleep(10);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }

    }

    public void saleTicket()
    {
        while (true)
        {
            if (ticket > 0)
            {
                System.out.println(Thread.currentThread().getName() + "  正在卖第" + ticket + "张票");
                ticket--;
            }
            else
            {
                System.out.println("余票不足!");
                break;
            }
        }
    }

    public static void main(String[] args)
    {
        Multithread mt = new Multithread();
        new Thread(mt, "窗口1").start();
        new Thread(mt, "窗口2").start();
        new Thread(mt, "窗口3").start();
        new Thread(mt, "窗口4").start();
    }
}

利用Synchroinzed 关键字进行同步锁

public class Multithread implements Runnable
{

    //总票数
    private int ticket = 100;

    //同步锁对象
    Object obj = new Object();

    /**
     * 利用synchronized关键字对代码块进行同步
     * 保证同一时间只有一个线程在操作共享变量 其余线程则等待该线程执行完毕后再执行
     * obj 对象锁可以是任意类型
     * 注意: 要保证对象锁是同一个
     */
    @Override
    public void run()
    {
        saleTicket();
        try
        {
            //线程睡眠10毫秒 每个线程都有执行机会
            Thread.sleep(10);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }

    }

    public void saleTicket()
    {
        while (true)
        {
            synchronized (obj){if (ticket > 0)
            {
                System.out.println(Thread.currentThread().getName() + "  正在卖第" + ticket + "张票");
                ticket--;
            }
            else
            {
                System.out.println("余票不足!");
                break;
            }
            }
        }
    }

    public static void main(String[] args)
    {
        Multithread mt = new Multithread();
        new Thread(mt, "窗口1").start();
        new Thread(mt, "窗口2").start();
        new Thread(mt, "窗口3").start();
        new Thread(mt, "窗口4").start();
    }
}

通过synchronized关键字对代码块进行同步(加锁),保证共享的变量同一时间只能有一个线程在操作。其他线程则等待该线程执行完毕后在进行操作,这样就保证了线程的同步和共享数据的安全!

注意

synchronized 同步代码块的锁可以是任意类对象(继承Thread类),这个对象必须是线程共享类(静态的)
synchronized 可以加在方法上,如果是静态方法synchronized的锁就是这个类的类对象,如果不是静态方法,synchronized 加在对象方法上,这个锁就是this


(未完待续)

时间: 2024-08-16 21:20:40

java-多线程 | 线程安全和线程同步的相关文章

Java多线程编程中synchronized线程同步的教程_java

0.关于线程同步 (1)为什么需要同步多线程?线程的同步是指让多个运行的线程在一起良好地协作,达到让多线程按要求合理地占用释放资源.我们采用Java中的同步代码块和同步方法达到这样的目的.比如这样的解决多线程无固定序执行的问题: public class TwoThreadTest { public static void main(String[] args) { Thread th1= new MyThread1(); Thread th2= new MyThread2(); th1.sta

java多线程总结一:线程的两种创建方式及优劣比较

http://blog.csdn.net/touch_2011/article/details/6891026 1.通过实现Runnable接口线程创建 (1).定义一个类实现Runnable接口,重写接口中的run()方法.在run()方法中加入具体的任务代码或处理逻辑. (2).创建Runnable接口实现类的对象. (3).创建一个Thread类的对象,需要封装前面Runnable接口实现类的对象.(接口可以实现多继承) (4).调用Thread对象的start()方法,启动线程 示例代码

详解Java多线程编程中的线程同步方法_java

1.多线程的同步: 1.1.同步机制:在多线程中,可能有多个线程试图访问一个有限的资源,必须预防这种情况的发生.所以引入了同步机制:在线程使用一个资源时为其加锁,这样其他的线程便不能访问那个资源了,直到解锁后才可以访问. 1.2.共享成员变量的例子:成员变量与局部变量: 成员变量: 如果一个变量是成员变量,那么多个线程对同一个对象的成员变量进行操作,这多个线程是共享一个成员变量的. 局部变量: 如果一个变量是局部变量,那么多个线程对同一个对象进行操作,每个线程都会有一个该局部变量的拷贝.他们之间

java多线程学习笔记之线程安全

假设i为银行的存款数据,i = 100, 现在使用A B两个线程(两个人)同时执行 i = i + 10;(存10块钱)的操作,由于默认情况下异步执行,那么流程可能会是这样: 1. A线程从系统内存中读到 i. 2. B线程从系统内存中读到 i. 3. A线程执行 i+10. 4. B线程执行 i+10. 5. A线程将110写回到系统内存,内存中i的值为 110. 6. B线程将110写回到系统内存,内存中i的值为 110,. 显然这程结果是不正确的,少了一个人存的钱. synchronize

Java多线程编程基础之线程对象

在进入java平台的线程对象之前,基于基础篇(一)的一些问题,我先插入两个基本概念. [线程的并发与并行] 在单CPU系统中,系统调度在某一时刻只能让一个线程运行,虽然这种调试机制有多种形式(大多数是时间片轮巡为主),但无论如何,要通过不断切换需要运行的线程让其运行的方式就叫并发(concurrent).而在多CPU系统中,可以让两个以上的线程同时运行,这种可以同时让两个以上线程同时运行的方式叫做并行(parallel). 在上面包括以后的所有论述中,请各位朋友谅解,我无法用最准确的词语来定义储

java 多线程-下面两个方法同步吗,请说明理由,有什么方法可以验证?

问题描述 下面两个方法同步吗,请说明理由,有什么方法可以验证? class Test { synchronized static void say Hello3() { } synchronizedvoid getX() {} } 解决方案 现实应用如下场景: 一个人名叫王X的人 暗地销售火车票,数量为 SUM=1000; 某个时刻 用户甲从他那里购买了2张. 某个时刻 用户乙从他那里购买了4张. 某个时刻 用户丙从他那里购买了7张. ............... 购买者必须轮流购买火车票.

java多线程编程之向线程传递数据的三种方法_java

在传统的同步开发模式下,当我们调用一个函数时,通过这个函数的参数将数据传入,并通过这个函数的返回值来返回最终的计算结果.但在多线程的异步开发模式下,数据的传递和返回和同步开发模式有很大的区别.由于线程的运行和结束是不可预料的,因此,在传递和返回数据时就无法象函数一样通过函数参数和return语句来返回数据.本文就以上原因介绍了几种用于向线程传递数据的方法,在下一篇文章中将介绍从线程中返回数据的方法. 欲先取之,必先予之.一般在使用线程时都需要有一些初始化数据,然后线程利用这些数据进行加工处理,并

Java多线程编程基础之线程和多线程

[写在前面] 随着计算机技术的发展,编程模型也越来越复杂多样化.但多线程编程模型 是目前计算机系统架构的最终模型.随着CPU主频的不断攀升,X86架构的硬件已 经成为瓶,在这种架构的CPU主频最高为4G.事实上目前3.6G主频的CPU已经接近了顶峰. 如果不能从根本上更新当前CPU的架构(在很长一段时间内还不太可能),那么 继续提高CPU性能的方法就是超线程CPU模式.那么,作业系统.应用程序要发挥 CPU的最大性能,就是要改变到以多线程编程模型为主的并行处理系统和并发式 应用程序. 所以,掌握

java多线程的同步 通信以及生产消费者问题

Demo1 /*   Runable接口比直接从Thread继承方便的多  .  *    new  Thread(...) ;这样即使我们传递了同一个实现了Runnable接口的多个对象那么 也是多个线程 ,而且多个线程共享数据域. *    否则new Thread 创建的多个线程之间 互不相干  ,数据之间互不干涉 *    同步就是为了实现 在多个线程同时对一个资源进行操作的时候 要一个一个的执行 , *    只有等占有CPU的 线程完事之后 其他等待线程才能进入就绪状态等待运行 * 

java多线程和并发包入门示例_java

一.java多线程基本入门java多线程编程还是比较重要的,在实际业务开发中经常要遇到这个问题. java多线程,传统创建线程的方式有两种. 1.继承自Thread类,覆写run方法. 2.实现Runnable接口,实现run方法. 启动线程的方法都是调用start方法,真正执行调用的是run方法.参考代码如下: 复制代码 代码如下: package com.jack.thread; /** * 线程简单演示例子程序 *  * @author pinefantasy * @since 2013-