第一个例子是同一实例方法加锁和不加锁在多线程情况下的访问情况,代码如下:
package test.caipiao.log; /** * 一个类的同一个实例,方法加锁和不加锁情况下的访问情况. * @author lfl * */ public class Test { public static void main(String[] args) { final TT tt = new TT(); Thread t1 = new Thread() { public void run() { tt.test(); //调用加锁的test() } }; Thread t2 = new Thread() { public void run() { tt.test2(); //调用加锁的test2() } }; Thread t3 = new Thread() { public void run() { tt.test3(); //调用不加锁的test3() } }; /** * t1 t2 t3 三个线程依次启动运行. */ t1.start(); t2.start(); t3.start(); System.out.println("over"); } } class TT { public synchronized void test() { for (int i = 0; i < 10000000; i++) { if (i % 10000 == 0) { System.out.println(i + " --- "); } } } public synchronized void test2() { System.out.println("test2"); } public void test3() { System.out.println("test3"); } }
输出如下(多次运行和不同计算机上运行输出结果可能不一样):
over
0 ---
test3
10000 ---
20000 ---
30000 ---
40000 ---
50000 ---
60000 ---
......
9910000 ---
9920000 ---
9930000 ---
9940000 ---
9950000 ---
9960000 ---
9970000 ---
9980000 ---
9990000 ---
test2
说明:
主线程先输出 over,主线程没有持有tt上的锁,这里就不关心了。
t1线程输出0,此时还在持有tt上的锁,t3线程就输出test3,说明线程t1持有tt上的锁,而不影响t3线程调用test3()方法,即允许其它线程方法访问该实例的非加锁方法。而最后输出test2,是在线程t1释放了tt上的锁后
线程t3获得tt上的锁才能调用test2()方法,即一个线程持有当前实例的锁,其它线程不能访问该实例的加锁方法。
-------------------------------分割线-------------------------------------------------
第二个例子是静态方法加锁和不加锁在多线程情况下的访问情况,代码如下:
package test.caipiao.log; /** * 一个类的同一个实例,方法加锁和不加锁情况下的访问情况. * @author lfl * */ public class TestStatic { public static void main(String[] args) { final TTStatic tt = new TTStatic(); Thread t1 = new Thread() { public void run() { TTStatic.test(); //调用加锁的test() } }; Thread t2 = new Thread() { public void run() { TTStatic.test2(); //或者 tt.test2(); //调用加锁的test2() } }; Thread t3 = new Thread() { public void run() { TTStatic.test3(); //调用不加锁的test3() } }; /** * t1 t2 t3 三个线程依次启动运行. */ t1.start(); t2.start(); t3.start(); System.out.println("over"); } } class TTStatic { public static synchronized void test() { for (int i = 0; i < 10000000; i++) { if (i % 10000 == 0) { System.out.println(i + " --- "); } } } public static synchronized void test2() { System.out.println("test2"); } public static void test3() { System.out.println("test3"); } }
输出如下(多次运行和不同计算机上运行输出结果可能不一样):
0 ---
10000 ---
20000 ---
30000 ---
40000 ---
50000 ---
60000 ---
70000 ---
80000 ---
90000 ---
test3
over
100000 ---
110000 ---
120000 ---
130000 ---
......
9920000 ---
9930000 ---
9940000 ---
9950000 ---
9960000 ---
9970000 ---
9980000 ---
9990000 ---
test2
test2
说明:
基本同第一个例子的分析一样,只不过这里线程持有的是TTStatic class对象上的锁,而TTStatic class对象只有一个。需要说明的是最后输出的两个test2,这个说明调用静态的加锁方法,不论是用类名调用还是实例调用,都需要获得该类的class对象上的锁。
-------------------------------------------分割线------------------------------------------------------
第三个例子是同一实例方法加锁和不加锁在多线程情况下和静态方法加锁和不加锁在多线程情况下的访问情况,代码如下:
package test.caipiao.log; /** * 一个类的同一个实例,方法加锁和不加锁情况下的访问情况. * @author lfl * */ public class TestHybrid { public static void main(String[] args) { Thread t1 = new Thread() { public void run() { TTHybrid.test(); //调用加锁的test() } }; Thread t2 = new Thread() { public void run() { TTHybrid.test2(); //调用加锁的test2() } }; Thread t3 = new Thread() { public void run() { TTHybrid.test3(); //调用不加锁的test3() } }; //下面是非静态方法调用 final TTHybrid tt = new TTHybrid(); Thread t4 = new Thread() { public void run() { tt.test4(); //调用加锁的test4() } }; Thread t5 = new Thread() { public void run() { tt.test5(); //调用加锁的test5() } }; Thread t6 = new Thread() { public void run() { tt.test6(); //调用不加锁的test6() } }; /** * t1 t2 t3 三个线程依次启动运行. */ t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); t6.start(); System.out.println("over"); } } class TTHybrid { public static synchronized void test() { for (int i = 0; i < 10000000; i++) { if (i % 10000 == 0) { System.out.println(i + " --- static"); } } } public static synchronized void test2() { System.out.println("test2"); } public static void test3() { System.out.println("test3"); } public synchronized void test4() { for (int i = 0; i < 10000000; i++) { if (i % 10000 == 0) { System.out.println(i + " --- instance"); } } } public synchronized void test5() { System.out.println("test5"); } public void test6() { System.out.println("test6"); } }
输出如下(多次运行和不同计算机上运行输出结果可能不一样):
0 --- static
over
test3
0 --- instance
10000 --- static
10000 --- instance
20000 --- instance
30000 --- instance
20000 --- static
40000 --- instance
50000 --- instance
30000 --- static
60000 --- instance
40000 --- static
50000 --- static
60000 --- static
70000 --- instance
test6
90000 --- static
100000 --- instance
100000 --- static
110000 --- static
120000 --- static
110000 --- instance
130000 --- static
120000 --- instance
140000 --- static
130000 --- instance
150000 --- static
......
9960000 --- instance
9370000 --- static
9970000 --- instance
9380000 --- static
9980000 --- instance
9390000 --- static
9990000 --- instance
9400000 --- static
9410000 --- static
test5
9420000 --- static
9430000 --- static
......
9970000 --- static
9980000 --- static
9990000 --- static
test2
说明:
主线程输出的over,t3线程输出的test3和t6线程输出的test6,可以看到无论是否是静态的,持有相应的锁对该类方法的访问没有影响。接着可以看到test()和test4()方法,基本是交替调用输出的,这说明持有TTHybrid class 对象上的锁和持有tt上的锁是没有直接关系的,即持有TTHybrid class 对象上的锁访问静态方法不会影响持有tt上的锁访问非静态方法。test4()输出完毕后释放tt上的锁 后,test5()方法开始输出。test()输出完毕后释放TTHybrid class 对象上的锁 后,test2()方法开始输出。
通过这三个小例子应该对Java中实例方法和静态方法在多线程下加锁和不加锁的访问情况有了一定的了解。关于synchronized关键字的具体含义请参看其它相关资料。
相关推荐
在Java编程语言中,线程同步是一个至关重要的概念,特别是在多线程环境下,它用于确保多个线程在访问共享资源时能正确协调,避免数据不一致性和竞态条件。本实例65着重讲解了Java线程同步的实现方法,帮助开发者理解...
在Java中,实现多线程有两种主要方式:通过实现`Runnable`接口和继承`Thread`类。 首先,让我们从实现`Runnable`接口开始。在Java中,任何对象都可以成为线程,只需要该对象实现`Runnable`接口。我们定义一个类,...
这样,在多线程环境下,这个方法并不是线程平安的。 相比之下,print2() 方法使用了 synchronized 关键字,并锁住了整个类,这样可以实现线程平安。 ``` public void print2(int v) { synchronized (MyThread....
Java多线程与同步是Java编程中的核心概念,它们在构建高效、响应迅速的应用程序时起着至关重要的作用。在Java中,多线程允许同时执行多个代码段,从而提高程序的执行效率,特别是在处理I/O密集型或计算密集型任务时...
在本资源中,我们将深入探讨Java中实现和管理多线程的方法,以及与多线程相关的各种概念和问题。 首先,需要理解进程和线程的基本概念。进程是计算机中正在执行的一个程序的实体,例如一个.class文件或一个.exe文件...
Java对象锁和类锁是Java多线程编程中至关重要的概念,它们是通过`synchronized`关键字来实现的,用于确保代码在并发环境下的线程安全。在这个全面解析中,我们将深入探讨这两个锁机制,理解它们的工作原理以及如何在...
在Java编程中,线程同步和线程协作是多线程编程的重要概念,确保了在并发环境下程序的正确性和数据的一致性。线程同步的主要目标是解决线程安全问题,即在多线程访问共享资源时避免数据的混乱,保证程序的可再现性。...
在Java中,通过创建`Thread`对象来实现多线程。 ##### 1.2 创建线程的方式 - **继承Thread类**:创建一个Thread子类,并重写run()方法。 - **实现Runnable接口**:定义一个实现Runnable接口的类,并重写run()方法...
Java中的`synchronized`关键字是用于实现线程同步的关键机制,主要目的是解决多线程环境下的数据安全问题。当多个线程访问共享资源时,如果没有适当的同步控制,可能会导致数据不一致或者竞态条件等问题。线程同步...
- **常见多线程问题及其解决方法**:多线程编程中常见的问题包括内存可见性问题、竞争条件、死锁、活锁和饥饿等。这些问题通常可以通过使用合适的同步机制(如锁、原子变量)、设计合理的线程间通信机制来解决。
在多线程环境中,实现线程安全的Singleton至关重要,因为不正确的实现可能导致多个实例的创建,违背了Singleton的基本原则。 1. 寂寞的Singleton Singleton通常采用静态内部类、枚举或懒汉式(Lazy Initialization...
### Java多线程知识点总结及企业真题解析 #### 一、知识点总结 ##### (1)多线程相关概念 1. **程序、进程和线程的区分**: - **程序**:为了完成特定的任务而编写的指令集合。它是静态的概念。 - **进程**:...
Java多线程编程中,线程安全问题是常见的挑战,特别是在并发环境下,多个线程可能会同时访问和修改同一份共享资源,导致数据不一致。为了解决这个问题,Java提供了同步机制,其中一种常用的方式就是同步代码块...
- **性能**:在某些情况下,`Lock`提供的锁可能比`synchronized`具有更好的性能,尤其是在需要频繁加锁和解锁的场景中。 ### 示例代码分析 接下来,我们来看一个使用`ReentrantLock`的例子,以展示如何使用`Lock`...
在Java多线程编程中,`synchronized`有多种用法,包括修饰实例方法、静态方法以及作为代码块来锁定特定的对象引用。 1. **修饰实例方法**: 当`synchronized`关键字用于实例方法时,它会对该实例对象加锁。这意味...
线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。 三、volatile 的原理和作用 ...
在Java编程语言中,"锁"是实现多线程并发控制的关键机制,它确保了共享资源的安全访问。本文将深入探讨Java中的锁以及相关的线程安全数据结构。 首先,我们来理解什么是锁。在多线程环境下,当多个线程试图访问和...
在类的实例方法中,可以直接访问实例变量和非静态方法,但不能直接访问静态方法或静态变量,因为静态成员属于类而不是实例。题目中给出了一个类A及其成员变量和方法的定义,询问了哪些调用是错误的,这涉及到对Java...
在Java编程语言中,`synchronized`关键字是并发编程中的关键概念,它用于控制多线程对共享资源的访问,以避免数据不一致性和线程安全问题。本篇文章将深入探讨`synchronized`的使用及其在Java并发编程中的作用。 ...