synchronized基本使用
对于线程来说,如果多个线程只是相互间单独执行的话,本身是没有太大意义的,一般来说,都是需要多个线程,相互间协作来进行工作的,这样使用,才会对系统带来实际的意义。
在Java中支持多个线程同时访问一个对象或对象中的成员变量,但是如果没有任何保护机制的话,就会出现数据不一致的情况,效果如下:
public class TestThread {
public static void main(String[] args) {
TestThread testThread = new TestThread();
new Thread(new Runnable() {
@Override
public void run() {
testThread.print();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
testThread.print();
}
}).start();
}
public void print(){
for(int i=0;i<1000;i++){
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
}
}
现在要解决这个问题的话,就可以使用锁来进行解决。synchronized可以修饰方法或者以同步块的形式来进行使用,它主要确保多个线程在同一个时刻,只能有一个线程处于方法或者同步块中,它保证了线程对变量访问的可见性和排他性,又称为内置锁机制。
对象锁
在同步代码块(或者同步方法)中拥有同一个对象锁的线程,如果有一个线程获得了锁,其他线程只能等该线程释放锁后才可以获得锁。
this锁对象
public class TestThread {
public static void main(String[] args) {
TestThread testThread = new TestThread();
new Thread(new Runnable() {
@Override
public void run() {
testThread.print();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
testThread.print();
}
}).start();
}
//同步代码块
public void print(){
synchronized (this){
for(int i=0;i<1000;i++){
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
}
}
//同步方法,synchronized 默认this作为锁对象
/* public synchronized void print(){
for(int i=0;i<1000;i++){
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
}*/
}
常量锁对象
public class TestThread {
public static void main(String[] args) {
TestThread testThread = new TestThread();
new Thread(new Runnable() {
@Override
public void run() {
testThread.print();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
testThread.print();
}
}).start();
//调用静态方法
new Thread(new Runnable() {
@Override
public void run() {
TestThread.print2();
}
}).start();
}
private static final Object obj = new Object();
public void print(){
synchronized (obj){
for(int i=0;i<1000;i++){
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
}
}
public static void print2(){
synchronized (obj){
for(int i=0;i<1000;i++){
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
}
}
}
类锁
synchronized修饰静态方法时把运行时类作为锁
public class TestThread {
public static void main(String[] args) {
TestThread testThread = new TestThread();
new Thread(new Runnable() {
@Override
public void run() {
testThread.print();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
testThread.print2();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
TestThread.print2();
}
}).start();
}
//将TestThread的字节码文件作为锁对象
public synchronized void print(){
synchronized (TestThread.class){
for(int i=0;i<1000;i++){
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
}
}
//synchronized修饰静态方法
public static synchronized void print2(){
for(int i=0;i<1000;i++){
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
}
}
以上三种方式都是将TestThread 的字节码文件作为锁对象,因此也可以实现同步
锁的可重入性
如果当前线程已经拥有锁对象,可以再次重复获得同一锁对象而不会出现死锁问题。
public class Reentrant {
public synchronized void function1(){
System.out.println("同步方法1");
function2();
}
public synchronized void function2(){
System.out.println("同步方法2");
function3();
}
public synchronized void function3(){
System.out.println("同步方法3");
}
public static void main(String[] args) {
Reentrant reentrant = new Reentrant();
new Thread(new Runnable() {
@Override
public void run() {
reentrant.function1();
}
}).start();
}
}