`

java笔记--关于线程同步(5种同步方式)

阅读更多
[size=medium]为何要使用同步?
    java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),
    将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,
    从而保证了该变量的唯一性和准确性。



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


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


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



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


    代码如:
   
synchronized(object){ 
    }


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

复制代码
package com.xhj.thread;
 
    /**
     * 线程同步的运用
     * 
     * @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();
        }
 
    }

    
3.使用特殊域变量(volatile)实现线程同步

    a.volatile关键字为域变量的访问提供了一种免锁机制,
    b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新,
    c.因此每次使用该域就要重新计算,而不是使用寄存器中的值
    d.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量
   
    例如:
        在上面的例子当中,只需在account前面加上volatile修饰,即可实现线程同步。
   
    代码实例:
   


      //只给出要修改的代码,其余代码与上同
       
class Bank {
            //需要同步的变量加上volatile
            private volatile int account = 100;
 
            public int getAccount() {
                return account;
            }
            //这里不再需要synchronized 
            public void save(int money) {
                account += money;
            }
        }

    注:多线程中的非同步问题主要出现在对域的读写上,如果让域自身避免这个问题,则就不需要修改操作该域的方法。
    用final域,有锁保护的域和volatile域可以避免非同步的问题。
   
4.使用重入锁实现线程同步

    在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。
    ReentrantLock类是可重入、互斥、实现了Lock接口的锁,
    它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力


    ReenreantLock类的常用方法有:

        ReentrantLock() : 创建一个ReentrantLock实例
        lock() : 获得锁
        unlock() : 释放锁
    注:ReentrantLock()还有一个可以创建公平锁的构造方法,但由于能大幅度降低程序运行效率,不推荐使用
       
    例如:
        在上面例子的基础上,改写后的代码为:
       
    代码实例:
   
//只给出要修改的代码,其余代码与上同
        class Bank {
            
            private int account = 100;
            //需要声明这个锁
            private Lock lock = new ReentrantLock();
            public int getAccount() {
                return account;
            }
            //这里不再需要synchronized 
            public void save(int money) {
                lock.lock();
                try{
                    account += money;
                }finally{
                    lock.unlock();
                }
                
            }
        }

         
    注:关于Lock对象和synchronized关键字的选择:
        a.最好两个都不用,使用一种java.util.concurrent包提供的机制,
            能够帮助用户处理所有与锁相关的代码。
        b.如果synchronized关键字能满足用户的需求,就用synchronized,因为它能简化代码
        c.如果需要更高级的功能,就用ReentrantLock类,此时要注意及时释放锁,否则会出现死锁,通常在finally代码释放锁
       
5.使用局部变量实现线程同步
    如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,
    副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。



    ThreadLocal 类的常用方法



   
ThreadLocal() : 创建一个线程本地变量 
    get() : 返回此线程局部变量的当前线程副本中的值 
    initialValue() : 返回此线程局部变量的当前线程的"初始值" 
    set(T value) : 将此线程局部变量的当前线程副本中的值设置为value




    例如:
        在上面例子基础上,修改后的代码为:
       
    代码实例:
       

//只改Bank类,其余代码与上同
        public class Bank{
            //使用ThreadLocal类管理共享变量account
            private static ThreadLocal<Integer> account = new ThreadLocal<Integer>(){
                @Override
                protected Integer initialValue(){
                    return 100;
                }
            };
            public void save(int money){
                account.set(account.get()+money);
            }
            public int getAccount(){
                return account.get();
            }
        }



    注:ThreadLocal与同步机制
        a.ThreadLocal与同步机制都是为了解决多线程中相同变量的访问冲突问题。
        b.前者采用以"空间换时间"的方法,后者采用以"时间换空间"的方式[/size]
分享到:
评论

相关推荐

    java-多线程中的同步与死锁笔记

    java-多线程中的同步与死锁笔记

    java笔记-从浅入深的入门学习

    同时,学习Java的并发编程,包括线程、同步机制(synchronized关键字和Lock接口)以及并发集合(如ConcurrentHashMap),能帮助你编写出多线程环境下的高效程序。 最后,Java的异常处理、垃圾回收机制以及内存管理...

    java笔记--

    - **Runnable接口**:另一种实现多线程的方式。 - **同步机制**:synchronized关键字、wait()、notify()和notifyAll(),防止数据竞争。 15. **网络编程** - **Socket编程**:实现客户端-服务器通信。 - **URL和...

    ---Java笔记---

    在Java编程语言中,ArrayList和LinkedList是两种常用的集合类,它们都属于List接口的实现,但各有特点,适用于不同的场景。 ArrayList是基于数组实现的,它内部维护了一个Object类型的动态数组。由于数组的特性,...

    Java多线程笔记

    Java多线程笔记是 Java 编程语言中关于多线程编程的笔记,涵盖了线程基础知识、线程优先级、线程状态、守护线程、构造线程、线程中断等多方面的内容。 获取简单 main 程序中的线程 在 Java 中,可以使用 ...

    Java分布式应用学习笔记05多线程下的并发同步器

    ### Java分布式应用学习笔记05多线程下的并发同步器 #### 1. 前言 在现代软件开发中,特别是在分布式系统和高性能计算领域,有效地管理多线程之间的协同工作至关重要。Java语言提供了丰富的工具和API来帮助开发者...

    Java多线程详解(超详细)_狂神说笔记完整版_项目代码_适合小白随课程学习

    - 创建线程有两种主要方式:继承`Thread`类并重写`run()`方法,或实现`Runnable`接口并提供`run()`方法,然后通过`Thread`构造函数创建线程对象。 - `start()`方法用于启动线程,`run()`方法包含线程执行的主要...

    JAVA笔记-针对于刚开始学习JAVA的有实质性帮助

    这些"JAVA笔记-针对于刚开始学习JAVA的有实质性帮助"包含了作者在实际开发中的经验总结,旨在为初学者提供有价值的指导。以下是对Java基础知识的详细阐述: 1. **Java简介**:Java是由Sun Microsystems(现为Oracle...

    Java多线程自学笔记

    #### 四、线程同步与死锁 - **资源竞争问题**: - 当多个线程访问共享资源时,可能会出现资源竞争的情况。 - 解决方案包括使用同步关键字如`synchronized`,或者显式锁`Lock`等。 - **死锁**: - 发生在两个或...

    Java中线程同步和线程协作学习笔记

    在Java编程中,线程同步和线程协作是多线程编程的重要概念,确保了在并发环境下程序的正确性和数据的一致性。线程同步的主要目标是解决线程安全问题,即在多线程访问共享资源时避免数据的混乱,保证程序的可再现性。...

    Java分布式应用学习笔记03JVM对线程的资源同步和交互机制

    ### Java分布式应用学习笔记03:JVM对线程的资源同步和交互机制 在深入探讨Java虚拟机(JVM)如何处理线程间的资源同步与交互机制之前,我们先来明确几个关键概念:线程、多线程、同步、并发以及它们在Java中的实现...

    个人笔记--Java_API

    集合是Java编程语言中用于存储和操作多个元素的一种容器。它提供了动态管理元素的能力,允许在运行时添加或删除元素。 **1.2 集合与数组** - **区别**: - **灵活性**:集合的大小可以动态改变,而数组一旦创建其...

    java多线程笔记

    线程同步是为了避免多线程环境下的数据竞争问题,Java提供了多种同步机制。同步方法通过`synchronized`关键字修饰,确保同一时间只有一个线程能访问该方法。同步块(Synchronized Block)更灵活,可以指定同步的代码...

    笔记14-JAVAEE之多线程初阶

    Java提供了`synchronized`关键字来实现线程同步,确保同一时刻只有一个线程能执行特定代码块。`volatile`关键字则用于确保变量在多线程环境中的可见性。 5. **线程池与协程** 为避免频繁创建和销毁线程带来的开销...

    java从入门到精通学习笔记-Lesson5(多线程).pdf

    Java 语言提供对多线程的内置支持,主要有两种创建线程的方式: 1. **继承 Thread 类**:创建一个新的类,继承自 Thread 类,并重写其 `run()` 方法。然后通过实例化这个类并调用 `start()` 方法来启动线程。 ```...

    JAVA 多线程学习笔记

    这篇学习笔记将深入探讨Java多线程的核心概念、实现方式以及相关工具的使用。 一、多线程基础 1. 线程与进程:在操作系统中,进程是资源分配的基本单位,而线程是程序执行的基本单位。每个进程至少有一个主线程,...

    day06 【线程、同步】-笔记.pdf

    在Java中,线程是一种实现多任务并行处理的方式,它允许程序的一部分独立于主程序运行。本节内容围绕Java中的线程以及如何在多线程环境下保持数据的一致性和安全性进行展开。 ### 线程的创建和运行原理 在Java中,...

    读书笔记-Java并发编程实战-基础篇

    同步可以通过以下三种方式实现:使变量不被共享(线程私有)、使变量不可变、或者在访问变量时使用同步。 5. 正确性和线程安全性:编写并发应用程序的步骤包括保证程序正确性和性能测试,如果需要,再对程序进行...

    java线程学习笔记

    在多线程环境中,资源共享是非常重要的,为了避免资源竞争和数据不一致的问题,可以使用 synchronized 关键字来实现线程同步。 引起线程阻塞挂起的原因: 1. Thread.sleep() 方法:线程睡眠一段时间,不会释放锁...

Global site tag (gtag.js) - Google Analytics