Java多线程编程中Synchronized关键字的使用实例

在 Java 多线程编程中,Synchronized 关键字是一个非常常用的同步机制,能够保证同一时间只有一个线程访问共享资源。本文将通过示例代码介绍 Synchronized 关键字的具体使用方法,以及在开发中的常见应用场景。

示例代码如下:

public class Example {
    private int count = 0;

    // 方法一:修饰方法
    public synchronized void add() {
        count++;
    }

    // 方法二:同步代码块
    public void print() {
        synchronized (this) {
            System.out.println("count: " + count);
        }
    }

    // 方法三:静态同步方法
    public static synchronized void sub() {
        count--;
    }

    public static void main(String[] args) {
        Example example = new Example();

        // 示例一:多个线程同时访问共享资源
        Runnable runnable = () -> {
            for (int i = 0; i < 1000000; i++) {
                example.add();
            }
        };
        Thread thread1 = new Thread(runnable);
        Thread thread2 = new Thread(runnable);
        thread1.start();
        thread2.start();
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("count: " + example.count);

        // 示例二:并发访问数据库
        Runnable runnable1 = () -> {
            synchronized (example) {
                // 访问数据库的代码
            }
        };
        Thread thread3 = new Thread(runnable1);
        Thread thread4 = new Thread(runnable1);
        thread3.start();
        thread4.start();

        // 示例三:实现单例模式
        public static synchronized Example getInstance() {
            if (instance == null) {
                instance = new Example();
            }
            return instance;
        }
    }
}

代码说明: 

  1. 在上述代码中,我们定义了一个 Example 类并在其中声明了一个成员变量 count,以及三个方法 add、print 和 sub。
  2. 方法一 add 使用 Synchronized 关键字修饰,保证了单线程访问,避免了数据竞争的问题。
  3. 方法二 print 使用同步代码块,锁对象为当前实例对象 this,同样能够保证线程安全。
  4. 方法三 sub 是一个静态同步方法,锁对象为 Example.class,用于保护静态的成员变量。
  5. 在 main 方法中,我们通过示例代码介绍了 Synchronized 关键字的应用场景:
    • 示例一:多个线程同时访问共享资源 count,通过 Synchronized 关键字保证线程安全;
    • 示例二:并发访问数据库,使用 Synchronized 关键字保证数据的一致性;
    • 示例三:实现单例模式,使用 Synchronized 关键字避免同时创建多个实例对象。

需要注意的是,在实际开发过程中,Synchronized 关键字的应用场景非常广泛,并且对于同步代码块来说,锁的范围应该尽可能小,以避免影响程序运行效率。

在 Java 中使用 Synchronized 关键字时,锁的粒度和锁的释放都非常重要

锁的粒度:锁的范围应该尽可能小,以避免出现死锁等问题。如果锁住了整个方法或者类,将会导致程序的运行效率降低。因此,我们应该尽可能仅在必要的代码段上加锁。

锁的释放:如果在加锁的代码段中因为异常等情况提前退出了,那么在退出之前一定要记得显式地释放锁,否则将会导致锁无法被其他线程获取,从而影响程序的正常运行。

下面是一个示例,演示了锁的释放机制:

public class Example {
    private Object lock = new Object();

    public void add() {
        synchronized (lock) {
            // 加锁
            try {
                // 模拟耗时操作
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "执行结束,即将释放锁");
        }
    }

    public static void main(String[] args) {
        Example example = new Example();

        // 启动多个线程并发访问共享资源
        for (int i = 0; i < 5; i++) {
            new Thread(() -> example.add()).start();
        }
    }
}

在上面的代码中,我们定义了一个锁对象 lock,并在 add 方法中使用 synchronized (lock) 进行加锁。由于模拟了耗时操作,因此执行结束后显示释放锁。

运行该程序,我们可以看到如下输出:

Thread-2执行结束,即将释放锁
Thread-0执行结束,即将释放锁
Thread-1执行结束,即将释放锁
Thread-3执行结束,即将释放锁
Thread-4执行结束,即将释放锁

可以发现,每个线程都执行完毕之后显示了即将释放锁的提示信息,说明锁已经被成功释放。

除了 Synchronized 关键字以外,Java 中还有以下常用的同步机制

1、Lock

Lock 是 Java.util.concurrent 包中提供的锁接口,它可以替代 synchronized 来实现多线程间的互斥和同步。Lock 接口提供了以下三个重要的方法:

  • lock(): 尝试获取锁,如果锁已经被其他线程获取,则当前线程会进入休眠状态,直到获取锁成功。
  • unlock(): 释放锁,通知其他线程可以获取锁了。
  • tryLock(): 尝试获取锁,如果获取失败则立即返回 false,否则返回 true。

使用 Lock 时需要注意以下事项:

  • 必须手动加锁和解锁,即在 try...finally 块中使用 lock() 和 unlock() 方法。
  • 如果线程无法获取到锁,则可以使用 tryLock() 方法进行尝试,避免因为等待时间过长导致时间浪费。

2、Semaphore

Semaphore(信号量)是一种同步工具,它可以限制同时访问某个资源的线程数量。Semaphore 维护了一个计数器,可以通过 acquire() 方法获取资源,而资源的数量则通过 release() 方法释放。

Semaphore 的主要作用:

  • 对线程数量进行控制:Semaphore 可以限制同时访问某个资源的线程数量,避免这些线程之间的操作产生竞争和冲突。
  • 控制多个共享资源的访问:Semaphore 可以用来实现限制数据库连接池的大小、同时访问某个共享文件的线程数量等。

Semaphore 使用示例:

public class Example {
    private Semaphore semaphore = new Semaphore(3); // 限制同时最多有3个线程访问共享资源

    public void access() {
        try {
            semaphore.acquire(); // 获取资源
            // 访问共享资源
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            semaphore.release(); // 释放资源
        }
    }

    public static void main(String[] args) {
        Example example = new Example();

        // 启动多个线程并发访问共享资源
        for (int i = 0; i < 5; i++) {
            new Thread(() -> example.access()).start();
        }
    }
}

在上述代码中,我们初始化了一个 Semaphore,限制最多同时有3个线程访问共享资源。在 access 方法中,使用 acquire() 方法获取资源后执行访问共享资源的操作,最后调用 release() 方法释放资源。

以上就是 Java 中常用的同步机制 Lock 和 Semaphore 的介绍及使用示例。需要注意的是,在使用这些同步机制时,都需要仔细考虑锁的粒度和锁的释放问题,以避免因线程之间的相互等待导致程序性能下降或者死锁等问题的出现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java 多线程可以通过创建 Thread 类的实例来实现。具体步骤如下: 1. 创建一个实现 Runnable 接口的类,该类实现 run() 方法。 ```java public class MyRunnable implements Runnable { public void run() { // 执行线程的操作 } } ``` 2. 创建一个 Thread 实例,并将实现了 Runnable 接口的类作为构造函数的参数。 ```java MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable); ``` 3. 启动线程。 ```java thread.start(); ``` 此外,还可以通过继承 Thread 类来实现多线程。具体步骤如下: 1. 创建一个继承自 Thread 类的类,重写 run() 方法。 ```java public class MyThread extends Thread { public void run() { // 执行线程的操作 } } ``` 2. 创建 MyThread 的实例。 ```java MyThread myThread = new MyThread(); ``` 3. 启动线程。 ```java myThread.start(); ``` 在多线程使用,需要注意线程安全的问题,比如共享变量的访问、同步操作等。可以使用 synchronized 关键字或者 Lock 接口来保证线程安全。 ### 回答2: 在Java多线程使用是指程序同时运行多个线程,每个线程执行自己的任务。Java多线程使用可以带来以下几点好处: 1. 提高程序的效率:多线程可以对多个任务进行并发处理,提高程序的运行效率。例如,可以将网络请求和UI界面分别放在两个不同的线程,这样即使网络请求比较耗时,UI界面也能进行响应,不会出现界面假死的情况。 2. 充分利用系统资源:多线程可以充分利用系统的处理器资源,提高系统的利用率。在多核处理器上运行多个线程,可以让每个核心都得到充分利用,提高系统的整体性能。 3. 实现异步编程多线程可以实现异步编程,即一个线程执行后续操作,不需要等待另一个线程的完成。这样可以提高程序的响应速度。例如,可以使用多线程来进行文件下载,下载过程可以同时进行其他操作。 4. 处理复杂的并发情况:在一些需要处理多个并发操作的场景多线程可以提供更好的解决方案。例如,在并发访问共享资源的情况下,使用线程锁可以保证对共享资源的安全访问,避免数据冲突和一致性问题。 Java使用多线程可以通过创建Thread类的实例或者实现Runnable接口来实现。通过继承Thread类来创建线程,需要重写run方法,在run方法定义线程要执行的任务。通过实现Runnable接口来创建线程,需要实现run方法,并将实现了Runnable接口的对象作为参数传递给Thread类的构造方法。 总之,Java多线程使用使得程序可以同时执行多个任务,提高了程序的效率和用户体验,并且能够处理复杂的并发情况。但需要注意多线程的安全性和线程之间的协作,避免出现数据冲突和一致性问题。 ### 回答3: Java多线程使用是指在一个程序同时执行多个任务或者同时处理多个请求。多线程可以提高程序的并发性和响应性,可以将耗时的操作和任务分配给不同的线程来执行,从而提高程序的运行效率。 在Java多线程使用主要依靠Thread类或者实现Runnable接口来创建线程。可以通过继承Thread类创建一个线程类,并重写run方法,在run方法定义需要执行的任务;也可以实现Runnable接口,创建一个Runnable对象,然后将该对象作为参数传递给Thread类的构造方法,创建一个线程对象。 使用多线程的好处是可以充分利用处理器的多核特性,同时进行多个任务,提高程序的运行效率。多线程还可以提高程序的响应性,当程序有耗时的操作时,可以将其放在一个独立的线程执行,防止主线程被阻塞,提高用户体验。 然而,多线程使用也存在一些问题。首先是线程安全问题,多个线程同时访问共享资源可能导致数据不一致或者数据损坏。为解决这个问题,可以使用同步机制,如synchronized关键字或Lock接口,保证在同一时间只有一个线程能够访问共享资源。其次,多线程的创建和销毁会消耗系统资源,如果线程数量过多,可能会影响系统性能。因此,在使用多线程时应该合理控制线程的数量。另外,线程之间的协调和通信也是一个值得关注的问题,可以使用wait、notify、join等方法来实现线程间的协作。 总之,Java多线程使用可以提高程序的并发性和响应性,但需要注意线程安全、资源消耗以及线程协调和通信等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值