线程入门-线程同步浅析

简介: #前言 刚实习的时候,当遇到数据量大并对效率要求高的业务时,就开始尝试学习如何使用多线程来处理。现在与大家分享一下。大家说到多线程,总有一个绕不开的问题,就是如何实现多线程的同步。大致总结了2个大家常用的方式:synchronized关键字与java.util.concurrent.locks.Lock接口。 #synchronized关键字 synchronized关键

前言

刚实习的时候,当遇到数据量大并对效率要求高的业务时,就开始尝试学习如何使用多线程来处理。现在与大家分享一下。大家说到多线程,总有一个绕不开的问题,就是如何实现多线程的同步。大致总结了2个大家常用的方式:synchronized关键字与java.util.concurrent.locks.Lock接口。

synchronized关键字

synchronized关键字一般作用于代码块或者方法。根据场景又有所不同。

synchronized作用于代码块时

synchronized(this)

这种方式锁的是当前的对象,如以下代码

package thread;

      public class Thread1 implements Runnable {  
          public void run() {  
               synchronized(this) {  
                    for (int i = 0; i < 5; i++) {  
                         System.out.println(Thread.currentThread().getName() + " synchronized loop " + i); 
                         try {
                          Thread.sleep(100);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                    }  
               }  
          }  
          public static void main(String[] args) {  
               Thread1 target = new Thread1();  
               Thread threadA = new Thread(target, "threadA");  
               Thread threadB = new Thread(target, "threadB");  
               threadA.start();  
               threadB.start(); 
          } 
      }
  
      输出结果:

      threadA synchronized loop 0
      threadA synchronized loop 1
      threadA synchronized loop 2
      threadA synchronized loop 3
      threadA synchronized loop 4
      threadB synchronized loop 0
      threadB synchronized loop 1
      threadB synchronized loop 2
      threadB synchronized loop 3
      threadB synchronized loop 4

synchronized(object)

这种方式锁的是object存在heap中的内容,而非stack上的引用.我们来看两段代码

package thread;

public class Thread2 implements Runnable {
    private Vsrsion version;

    public void run() {
        synchronized (version) {
            for (int i = 0; i < 5; i++) {
                //version = new Vsrsion(i);
                version.content = i;
                System.out.println(Thread.currentThread().getName() + " synchronized loop " + i + "version" + version.content);
            }
        }
    }

    public static void main(String[] args) {
         Vsrsion a = new Vsrsion(0);
         Thread2 target = new Thread2(); 
         target.version = a;
         Thread threadA = new Thread(target, "threadA");  
         Thread threadB = new Thread(target, "threadB");  
         threadA.start();  
         threadB.start(); 
    }
}

class Vsrsion {
    int content;
    public Vsrsion(int content){
        this.content = content;
    }
}
输出结果:
threadA synchronized loop 0version0
threadA synchronized loop 1version1
threadA synchronized loop 2version2
threadA synchronized loop 3version3
threadA synchronized loop 4version4
threadB synchronized loop 0version0
threadB synchronized loop 1version1
threadB synchronized loop 2version2
threadB synchronized loop 3version3
threadB synchronized loop 4version4

因为2线程个线程是只是对version对象内的值修改,所以2个线程有序进行执行。

package thread;

public class Thread2 implements Runnable {
    private Vsrsion version;

    public void run() {
        synchronized (version) {
            for (int i = 0; i < 5; i++) {
                version = new Vsrsion(i);
                //version.content = i;
                System.out.println(Thread.currentThread().getName() + " synchronized loop " + i + "version" + version.content);
            }
        }
    }

    public static void main(String[] args) {
         Vsrsion a = new Vsrsion(0);
         Thread2 target = new Thread2(); 
         target.version = a;
         Thread threadA = new Thread(target, "threadA");  
         Thread threadB = new Thread(target, "threadB");  
         threadA.start();  
         threadB.start(); 
    }
}

class Vsrsion {
    int content;
    public Vsrsion(int content){
        this.content = content;
    }
}

输出结果:

threadB synchronized loop 0version0
threadB synchronized loop 1version1
threadA synchronized loop 0version0
threadB synchronized loop 2version2
threadB synchronized loop 3version3
threadA synchronized loop 1version1
threadB synchronized loop 4version4
threadA synchronized loop 2version2
threadA synchronized loop 3version3
threadA synchronized loop 4version4

从这里就能看出区别来了,因为线程里面的循环在heap创建新的对象,并将新的地址付给stack上的引用。所以2个线程其实只同步了第一次的version0,2个线程最后就乱序执行了。

synchronized何时产生作用

当一个synchronized(object)锁住object时,非synchronized代码块照样能操作这段代码块

package thread;

public class Thread2 implements Runnable {
    private Vsrsion version;

    public void run() {
        synchronized (version) {
            for (int i = 0; i < 5; i++) {
                version.content = i;
                try {
                    Thread.sleep((long) (Math.random()*100));
                } catch (InterruptedException e) {
                }
                System.out.println(Thread.currentThread().getName() + " version " + version.content);
            }
        }
    }

    public static void main(String[] args) {
         Vsrsion a = new Vsrsion(0);
         Thread2 target = new Thread2(); 
         target.version = a;
         Thread threadA = new Thread(target, "threadA");  
         threadA.start();  
         
         for (int i = 0; i<5; i++ ){
             a.content = i;
             System.out.println("loop version " + a.content);
             try {
                 Thread.sleep((long) (Math.random()*100));
             } catch (InterruptedException e) {
             }
         }
    }
}

class Vsrsion {
    int content;
    public Vsrsion(int content) {
        this.content = content;
    }
}

输出结果:

loop version 0
threadA version 0
loop version 1
threadA version 1
loop version 2
threadA version 2
loop version 3
loop version 4
threadA version 4
threadA version 4

所以当多个线程对某一资源做同步时,必须都加上synchronized关键字

1.2当synchronized作用于方法时

1.2.1.当方法为静态

例如:

class A{
    public static synchronized void medthod(){}
}
并等价于
class A{
    public void medthod(){
        synchonized(A.class){
            ....................
         }
    }
}

并未创建A对象并且要同步A中的static变量时,可以使用这种方式

1.2.2.该方法为非静态

package thread;

public class Thread3 implements Runnable {

    public synchronized void print(){
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " synchronized loop " + i);
            try {
                Thread.sleep((long) (Math.random()*100));
            } catch (InterruptedException e) {
            }
        }
    }
    
    public void run() {
        print();
    }

    public static void main(String[] args) {
         Thread3 target = new Thread3(); 
         Thread threadA = new Thread(target, "threadA");  
         Thread threadB = new Thread(target, "threadB");  
         threadA.start();  
         threadB.start(); 
    }
}

输出结果:
threadA synchronized loop 0
threadA synchronized loop 1
threadA synchronized loop 2
threadA synchronized loop 3
threadA synchronized loop 4
threadB synchronized loop 0
threadB synchronized loop 1
threadB synchronized loop 2
threadB synchronized loop 3
threadB synchronized loop 4

保证了这个类的方法在多个线程内得到同步。把 public synchronized void print()改成public static synchronized void print()结果不变。

java.util.concurrent.locks.Lock

关于这个接口的三个实现类,这里简单的讲下

ReentrantLock, 
Lock.lock()
Lock.unlock()
这个锁保证只有一个线程能对这块加锁的代码进行读和写操作

ReentrantReadWriteLock.ReadLock, 
lock.readLock()
lock.unlock()

ReentrantReadWriteLock.WriteLock
lock.wirteLock()
lock.unlock()
   /**
     * ReadWriteLock内置两个Lock,一个是读的Lock,一个是写的Lock。
     * 多个线程可同时得到读的Lock,但只有一个线程能得到写的Lock,
     * 而且写的Lock被锁定后,任何线程都不能得到Lock。ReadWriteLock提供的方法有:
     * readLock(): 返回一个读的lock 
     * writeLock(): 返回一个写的lock, 此lock是排他的。
     * ReadWriteLockTest很适合处理类似文件的读写操作。
     * 读的时候可以同时读,但不能写;写的时候既不能同时写也不能读。
     */  
目录
相关文章
|
14天前
|
安全 数据处理 开发者
Python中的多线程编程:从入门到精通
本文将深入探讨Python中的多线程编程,包括其基本原理、应用场景、实现方法以及常见问题和解决方案。通过本文的学习,读者将对Python多线程编程有一个全面的认识,能够在实际项目中灵活运用。
|
4天前
|
安全 Java 调度
Java中的多线程编程入门
【10月更文挑战第29天】在Java的世界中,多线程就像是一场精心编排的交响乐。每个线程都是乐团中的一个乐手,他们各自演奏着自己的部分,却又和谐地共同完成整场演出。本文将带你走进Java多线程的世界,让你从零基础到能够编写基本的多线程程序。
17 1
|
1月前
|
算法 NoSQL Java
Springboot3新特性:GraalVM Native Image Support和虚拟线程(从入门到精通)
这篇文章介绍了Spring Boot 3中GraalVM Native Image Support的新特性,提供了将Spring Boot Web项目转换为可执行文件的步骤,并探讨了虚拟线程在Spring Boot中的使用,包括如何配置和启动虚拟线程支持。
67 9
Springboot3新特性:GraalVM Native Image Support和虚拟线程(从入门到精通)
|
11天前
|
Java 数据处理 开发者
Java多线程编程的艺术:从入门到精通####
【10月更文挑战第21天】 本文将深入探讨Java多线程编程的核心概念,通过生动实例和实用技巧,引导读者从基础认知迈向高效并发编程的殿堂。我们将一起揭开线程管理的神秘面纱,掌握同步机制的精髓,并学习如何在实际项目中灵活运用这些知识,以提升应用性能与响应速度。 ####
36 3
|
12天前
|
Java
Java中的多线程编程:从入门到精通
本文将带你深入了解Java中的多线程编程。我们将从基础概念开始,逐步深入探讨线程的创建、启动、同步和通信等关键知识点。通过阅读本文,你将能够掌握Java多线程编程的基本技能,为进一步学习和应用打下坚实的基础。
|
30天前
|
安全 Java 开发者
在多线程编程中,确保数据一致性与防止竞态条件至关重要。Java提供了多种线程同步机制
【10月更文挑战第3天】在多线程编程中,确保数据一致性与防止竞态条件至关重要。Java提供了多种线程同步机制,如`synchronized`关键字、`Lock`接口及其实现类(如`ReentrantLock`),还有原子变量(如`AtomicInteger`)。这些工具可以帮助开发者避免数据不一致、死锁和活锁等问题。通过合理选择和使用这些机制,可以有效管理并发,确保程序稳定运行。例如,`synchronized`可确保同一时间只有一个线程访问共享资源;`Lock`提供更灵活的锁定方式;原子变量则利用硬件指令实现无锁操作。
19 2
|
2月前
|
安全 数据库连接 API
C#一分钟浅谈:多线程编程入门
在现代软件开发中,多线程编程对于提升程序响应性和执行效率至关重要。本文从基础概念入手,详细探讨了C#中的多线程技术,包括线程创建、管理及常见问题的解决策略,如线程安全、死锁和资源泄露等,并通过具体示例帮助读者理解和应用这些技巧,适合初学者快速掌握C#多线程编程。
76 0
|
3月前
|
Java
多线程线程同步
多线程的锁有几种方式
|
3月前
|
机器学习/深度学习 Java TensorFlow
深度学习中的图像识别:从理论到实践Java中的多线程编程入门指南
【8月更文挑战第29天】本文将深入探讨深度学习在图像识别领域的应用,从基础理论到实际应用案例,带领读者一步步理解如何利用深度学习技术进行图像识别。我们将通过一个简单的代码示例,展示如何使用Python和TensorFlow库实现一个基本的图像识别模型。无论你是初学者还是有一定经验的开发者,都能从中获得启发和学习。 【8月更文挑战第29天】在Java世界里,线程是程序执行的最小单元,而多线程则是提高程序效率和响应性的关键武器。本文将深入浅出地引导你理解Java多线程的核心概念、创建方法以及同步机制,帮助你解锁并发编程的大门。
|
3月前
|
安全 Java
【多线程面试题 六】、 如何实现线程同步?
实现线程同步的方法包括同步方法、同步代码块、使用ReentrantLock、volatile关键字以及原子变量类,以确保线程安全和数据一致性。