线程入门-线程同步浅析

简介: #前言 刚实习的时候,当遇到数据量大并对效率要求高的业务时,就开始尝试学习如何使用多线程来处理。现在与大家分享一下。大家说到多线程,总有一个绕不开的问题,就是如何实现多线程的同步。大致总结了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
AI 代码解读

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
AI 代码解读

因为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
AI 代码解读

从这里就能看出区别来了,因为线程里面的循环在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关键字
AI 代码解读

1.2当synchronized作用于方法时

1.2.1.当方法为静态

例如:

class A{
    public static synchronized void medthod(){}
}
并等价于
class A{
    public void medthod(){
        synchonized(A.class){
            ....................
         }
    }
}
AI 代码解读

并未创建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
AI 代码解读

保证了这个类的方法在多个线程内得到同步。把 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很适合处理类似文件的读写操作。
     * 读的时候可以同时读,但不能写;写的时候既不能同时写也不能读。
     */  
AI 代码解读
何度
+关注
目录
打赏
0
0
0
0
8
分享
相关文章
|
5月前
|
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
293 60
【Java并发】【线程池】带你从0-1入门线程池
Python中的多线程编程:从入门到精通
本文将深入探讨Python中的多线程编程,包括其基本原理、应用场景、实现方法以及常见问题和解决方案。通过本文的学习,读者将对Python多线程编程有一个全面的认识,能够在实际项目中灵活运用。
|
9月前
|
Springboot3新特性:GraalVM Native Image Support和虚拟线程(从入门到精通)
这篇文章介绍了Spring Boot 3中GraalVM Native Image Support的新特性,提供了将Spring Boot Web项目转换为可执行文件的步骤,并探讨了虚拟线程在Spring Boot中的使用,包括如何配置和启动虚拟线程支持。
512 9
Springboot3新特性:GraalVM Native Image Support和虚拟线程(从入门到精通)
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
8月前
|
Java中的多线程编程入门
【10月更文挑战第29天】在Java的世界中,多线程就像是一场精心编排的交响乐。每个线程都是乐团中的一个乐手,他们各自演奏着自己的部分,却又和谐地共同完成整场演出。本文将带你走进Java多线程的世界,让你从零基础到能够编写基本的多线程程序。
78 1
Java多线程编程的艺术:从入门到精通####
【10月更文挑战第21天】 本文将深入探讨Java多线程编程的核心概念,通过生动实例和实用技巧,引导读者从基础认知迈向高效并发编程的殿堂。我们将一起揭开线程管理的神秘面纱,掌握同步机制的精髓,并学习如何在实际项目中灵活运用这些知识,以提升应用性能与响应速度。 ####
99 3
Java中的多线程编程:从入门到精通
本文将带你深入了解Java中的多线程编程。我们将从基础概念开始,逐步深入探讨线程的创建、启动、同步和通信等关键知识点。通过阅读本文,你将能够掌握Java多线程编程的基本技能,为进一步学习和应用打下坚实的基础。
在多线程编程中,确保数据一致性与防止竞态条件至关重要。Java提供了多种线程同步机制
【10月更文挑战第3天】在多线程编程中,确保数据一致性与防止竞态条件至关重要。Java提供了多种线程同步机制,如`synchronized`关键字、`Lock`接口及其实现类(如`ReentrantLock`),还有原子变量(如`AtomicInteger`)。这些工具可以帮助开发者避免数据不一致、死锁和活锁等问题。通过合理选择和使用这些机制,可以有效管理并发,确保程序稳定运行。例如,`synchronized`可确保同一时间只有一个线程访问共享资源;`Lock`提供更灵活的锁定方式;原子变量则利用硬件指令实现无锁操作。
84 2
「Python入门」Python多线程
1. **线程与进程区别**:线程共享内存,进程独立;线程启动快,多线程效率高于多进程。 2. **多线程使用**:直接使用Thread类,通过`target`指定函数,`args`传递参数;或继承Thread,重写`run`方法。 3. **守护线程**:设置`setDaemon(True)`,主线程结束时,守护线程一同结束。 4. **join线程同步**:主线程等待子线程完成,如`t.join()`。 5. **线程锁**(Mutex):防止数据竞争,确保同一时间只有一个线程访问共享资源。 6. **RLock(递归锁)**:允许多次锁定,用于需要多次加锁的递归操作。
74 1
「Python入门」Python多线程
|
11月前
|
多线程线程同步
多线程的锁有几种方式
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问