Java多线程(4)--线程的同步解决线程安全问题

简介: Java多线程(4)--线程的同步解决线程安全问题

多线程出现安全问题


问题的原因:

当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。


解决办法:

对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。


Java对于多线程的安全问题提供了专业的解决方式:同步机制


方式一:同步代码块


synchronized(同步监视器){
  //需要被同步的代码
}


说明:

1.操作共享数据的代码,即为需要被同步的代码。

2.共享数据:多个线程共同操作的变量,比如: 车票就是共享数据。

3.同步监视器,俗称:锁,任何一个类的对象,都可以充当锁。(但是要求:多个线程必须共用同一把锁,即同一个对象)(可以考虑用this充当)(可以考虑类.class充当)


示例:

①Runnable接口实现类


public class MThread implements Runnable{
    private int ticket = 100;
    Object obj = new Object();
    @Override
    public void run() {
        while (true) {
            synchronized (obj) {
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + ":票号为" + ticket);
                    ticket--;
                } else break;
            }
        }
    }
}


②创建并执行多线程


public class TestThread {
    public static void main(String[] args) {
        MThread thread = new MThread();
        Thread curThread1 = new Thread(thread);
        Thread curThread2 = new Thread(thread);
        Thread curThread3 = new Thread(thread);
        curThread1.setName("窗口一");
        curThread2.setName("窗口二");
        curThread3.setName("窗口三");
        curThread1.start();
        curThread2.start();
        curThread3.start();
    }
}


同步的方式,解决了线程的安全问题。

操作同步代码时,只能有一 个线程参与, 其他线程等待,相当于是一个单线程的过程,效率低。


注意:在继承Thread类这种情况下,需要将同步监视器声明为静态的,这样才能保证同一把锁。


方式二:同步方法


synchronized还可以放在方法声明中,表示整个方法为同步方法。
public synchronized void show (){ //其实是有一个同步监视器(锁)this
  //需要被同步的代码
}


与同步代码块使用类似,只是在run()方法中调用该同步方法。


注意:和使用同步代码块一样,在继承Thread类这种情况下,需要将该同步方法声明为静态的,即public static synchronized void show (),此时的同步监视器相当于是当前类.class。


同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。

非静态的同步方法,同步监视器是: this

静态的同步方法,同步监视器是:当前类本身


方式三:Lock锁方式


从JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步,同步锁使用Lock对象充当。


ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。


class A{
  private final ReentrantLock lock = new ReenTrantLock();
  public void run(){
  lock.lock();
  try{
    //保证线程安全的代码;
  }finally{
    lock.unlock(); 
  }
  }
}


synchronized 与 Lock 的对比:


①Lock是显式锁(手动开启和关闭锁),synchronized是隐式锁,出了作用域自动释放。

②Lock只有代码块锁,synchronized有代码块锁和方法锁。

③使用Lock锁,JVM将花费较少的时间来调度线程,性能更好,并且具有更好的扩展性(提供更多的子类)。



相关文章
|
1天前
|
监控 Java 调度
Java并发编程:深入理解线程池
【6月更文挑战第26天】在Java并发编程的世界中,线程池是提升应用性能、优化资源管理的关键组件。本文将深入探讨线程池的内部机制,从核心概念到实际应用,揭示如何有效利用线程池来处理并发任务,同时避免常见的陷阱和错误实践。通过实例分析,我们将了解线程池配置的策略和对性能的影响,以及如何监控和维护线程池的健康状况。
7 1
|
1天前
|
存储 测试技术
【工作实践(多线程)】十个线程任务生成720w测试数据对系统进行性能测试
【工作实践(多线程)】十个线程任务生成720w测试数据对系统进行性能测试
9 0
【工作实践(多线程)】十个线程任务生成720w测试数据对系统进行性能测试
|
1天前
|
存储 缓存 Java
老程序员分享:Java并发编程:线程池的使用
老程序员分享:Java并发编程:线程池的使用
|
1天前
|
Java 数据库连接 调度
Java多线程,对锁机制的进一步分析
Java多线程,对锁机制的进一步分析
|
1天前
|
Java
Java多线程notifyAll()方法
Java多线程notifyAll()方法
|
2天前
|
数据采集 Java Unix
10-多线程、多进程和线程池编程(2)
10-多线程、多进程和线程池编程
|
2天前
|
安全 Java 调度
10-多线程、多进程和线程池编程(1)
10-多线程、多进程和线程池编程
|
7天前
|
存储 Linux C语言
c++进阶篇——初窥多线程(二) 基于C语言实现的多线程编写
本文介绍了C++中使用C语言的pthread库实现多线程编程。`pthread_create`用于创建新线程,`pthread_self`返回当前线程ID。示例展示了如何创建线程并打印线程ID,强调了线程同步的重要性,如使用`sleep`防止主线程提前结束导致子线程未执行完。`pthread_exit`用于线程退出,`pthread_join`用来等待并回收子线程,`pthread_detach`则分离线程。文中还提到了线程取消功能,通过`pthread_cancel`实现。这些基本操作是理解和使用C/C++多线程的关键。
|
9天前
|
安全 Java
【极客档案】Java 线程:解锁生命周期的秘密,成为多线程世界的主宰者!
【6月更文挑战第19天】Java多线程编程中,掌握线程生命周期是关键。创建线程可通过继承`Thread`或实现`Runnable`,调用`start()`使线程进入就绪状态。利用`synchronized`保证线程安全,处理阻塞状态,注意资源管理,如使用线程池优化。通过实践与总结,成为多线程编程的专家。
|
9天前
|
Java 开发者
告别单线程时代!Java 多线程入门:选继承 Thread 还是 Runnable?
【6月更文挑战第19天】在Java中,面对多任务需求时,开发者可以选择继承`Thread`或实现`Runnable`接口来创建线程。`Thread`继承直接但限制了单继承,而`Runnable`接口提供多实现的灵活性和资源共享。多线程能提升CPU利用率,适用于并发处理和提高响应速度,如在网络服务器中并发处理请求,增强程序性能。不论是选择哪种方式,都是迈向高效编程的重要一步。