java并发编程——线程同步

190 阅读3分钟

sysynchronized关键字可以修饰方法、代码块,但不能修饰构造器、成员变量等。

当sysynchronized关键字同来修饰方法和代码块时,能够保证同一时刻最多只有一个线程执行该段代码或方法。防止当有两个线程并发修改同一个文件时可能会造成异常。

同步代码块语法:

    synchronized(obj){
        ...
        //此处代码是同步代码块
    }

意思为:当线程要执行以下代码块之前,必须先获得对obj的锁。

当两个并发线程访问同一个对象object中的synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完代码块以后才能执行该代码块。

然而,另一个当一个线程访问object的一个synchronized(this)代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块,但是其他线程对object中其他所有的synchronized(this)同步代码块的访问将被阻塞

示例:

public class Thread1 {

    public void synchronize() {
        synchronized (this) {
            System.out.println("I am the synchronized part:");
            for (int i=0; i<3; i++) {
                System.out.println(Thread.currentThread().getName()+":"+(i+1));
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
            System.out.println("the synchronized part is over");
        }
    }

    public void synchronize2() {
        synchronized (this) {
            System.out.println("I am the synchronized part 2:");
            for (int i=0; i<3; i++) {
                System.out.println(Thread.currentThread().getName()+":"+(i+1));
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
            System.out.println("the synchronized part 2 is over");
        }
    }

    public void nonSynchronize() {
        System.out.println("non-synchronized part:");
        for (int i=0; i<3; i++) {
            System.out.println(Thread.currentThread().getName()+":"+(i+1));
            try {
                Thread.sleep(500);
            } catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        System.out.println("the non-synchronized part is over");
    }

    public static void main(String[] args) {
        final Thread1 thread1 = new Thread1();

    //启动线程,线程1、2和3分别用了不同的表达式
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                thread1.synchronize();
            }
        });
        Thread t2 = new Thread(() -> thread1.synchronize2());
        Thread t3 = new Thread(thread1::nonSynchronize);
        t1.start();
        t2.start();
        t3.start();
    }
}

输出结果:


从输出结果可以看出:线程0执行的是第一个同步代码块,线程2执行的是非同步代码块,线程0和2同时执行,两者互不干扰。线程1执行的是第二个同步代码块,因为线程0先获得对该对象的锁定,所以线程1即使执行代码块跟线程0不一样,也被阻塞,必须等线程1执行完同步代码块,释放锁,线程1才能继续执行。

同步方法示例

public class Thread1 {

    private synchronized void synchronize() {
        System.out.println("I am the synchronized method part:");
        for (int i=0; i<3; i++) {
            System.out.println(Thread.currentThread().getName()+":"+(i+1));
            try {
                Thread.sleep(500);
            } catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        System.out.println("the synchronized method part is over");
    }

    private synchronized void synchronize2() {
        System.out.println("I am the synchronized method part 2:");
        for (int i=0; i<3; i++) {
            System.out.println(Thread.currentThread().getName()+":"+(i+1));
            try {
                Thread.sleep(500);
            } catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        System.out.println("the synchronized method part 2 is over");
    }

    private  void nonSynchronize() {
        System.out.println("non-synchronized method part:");
        for (int i=0; i<3; i++) {
            System.out.println(Thread.currentThread().getName()+":"+(i+1));
            try {
                Thread.sleep(500);
            } catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        System.out.println("the non-synchronized method part is over");
    }

    public static void main(String[] args) {
        final Thread1 thread1 = new Thread1();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                thread1.synchronize();
            }
        });
        Thread t2 = new Thread(() -> thread1.synchronize2());
        Thread t3 = new Thread(thread1::nonSynchronize);
        t1.start();
        t2.start();
        t3.start();
    }
}

输出结果跟同步代码块一样:


同步锁

Lock 是控制多个线程对共享资源进行访问的工具。每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。

在实现线程安全的控制中,比较常用的是ReentrantLock。

class X {
    private final ReentrantLock lock = new ReentrantLock();
    //定义需要保证线程安全的方法
    public void m() {
        //加锁
        lock.lock();
        try {
            //method body
        } finally { //使用finally来保证释放锁
            lock.unlock();
        }
    }
}


参考资料:java加锁与同步方法

疯狂java讲义(第三版)

Java编程思想(第4版)