Java多线程之线程同步问题

线程同步

产生原因:每一个线程轮番抢占资源

1、不同步问题

//多个线程同时处理数据,不同步
class MyThread implements Runnable{
    private int ticket = 10;//总共是10张票
    @Override
    public void run() {
        while (this.ticket > 0){//还有余票
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"还有"+this.ticket--+"张票");
        }
    }
}
public class Test {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        new Thread(myThread,"线程1").start();
        new Thread(myThread,"线程2").start();
        new Thread(myThread,"线程3").start();
    }
}

执行以上代码之后我们会发现,票数可能会出现负数,这种问题称为不同步操作
不同步操作的优点是:处理速度快,多个线程并发执行
同步虽然可以保证数据的完整性,但是执行速度会很慢

2、synchronized处理同步问题

使用synchronized关键字处理同步问题有两种模式:同步代码块、同步方法
I、同步代码块

//使用同步代码块,必须设置一个要锁定的对象,一般锁定当前对象:this
class MyThread implements Runnable{
    private int ticket = 10;//总共是10张票
    @Override
    public void run() {//在方法中进行拦截
        for(int i = 0; i < 10; i++){
            //同一时刻,只允许一个线程进入代码块处理
            synchronized (this){//为程序逻辑上锁
                if(this.ticket > 0){
                    try {
                        Thread.sleep(100);//模拟网络延迟
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"还剩"+this.ticket--+"张票");
                }
            }
        }
    }
}
public class Test {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread thread1 = new Thread(myThread,"线程1");
        Thread thread2 = new Thread(myThread,"线程2");
        Thread thread3 = new Thread(myThread,"线程3");
//        thread1.setPriority(Thread.MIN_PRIORITY);//为线程设置优先级
//        thread2.setPriority(Thread.MAX_PRIORITY);
//        thread3.setPriority(Thread.MAX_PRIORITY);
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

II、同步方法

//同步方法
class MyThread implements Runnable{
    private int ticket = 10;//总共是10张票
    @Override
    public void run() {
        for(int i = 0; i < 10; i++){
            this.sale();
        }
    }
    public synchronized void sale(){
        if(this.ticket > 0){
            try {
                Thread.sleep(100);//模拟网络延迟
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"还剩"+this.ticket--+"张票");
        }
    }
}
public class Test {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread thread1 = new Thread(myThread,"线程1");
        Thread thread2 = new Thread(myThread,"线程2");
        Thread thread3 = new Thread(myThread,"线程3");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

关于Synchronized的说明

/synchronized锁多对象
class MyThread extends Thread{

    @Override
    public void run() {
        Sync sync = new Sync();
        sync.test();
    }
}
class Sync{
    public synchronized void test(){
        System.out.println("test方法开始,当前线程"+Thread.currentThread().getName());
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("test方法结束,当前线程"+Thread.currentThread().getName());
    }
}
public class Test {
    public static void main(String[] args) {
        for (int i = 0; i < 4; i++){
            Thread thread = new MyThread();
            thread.start();
        }
    }
}

此时的synchronized并没有起到作用,三个线程同时运行test()方法

实际上,synchronized(this)以及非static的synchronized方法,只能防止多个线程同时执行同一个对象的同步代码段,也就是说synchronized锁住的是括号里的内容而不是代码。对于非static的synchronized方法,锁的就是对象本身this。

当synchronized锁住同一个对象后,别的线程如果也想拿到这个对象锁,就必须等待这个线程执行完成释放锁,这样才能达到线程同步的目的。即使两个不同的代码段,都要锁同一个对象,那么这两个代码段也不能在多线程环境下同时运行

如果真要锁住这段代码,可以有以下两种方法
I、锁同一个对象

//synchronized锁同一个对象
class MyThread extends Thread{
    private Sync sync;
    public MyThread(Sync sync){
        this.sync = sync;
    }
    @Override
    public void run() {
        sync.test();
    }
}
class Sync{
    public void test(){
        synchronized(this){
            System.out.println("test方法开始,当前线程"+Thread.currentThread().getName());
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("test方法结束,当前线程"+Thread.currentThread().getName());
        }
    }
}
public class Test {
    public static void main(String[] args) {
        Sync sync = new Sync();
        for (int i = 0; i < 4; i++){
            Thread thread = new MyThread(sync);
            thread.start();
        }
    }
}

II、使用synchronized(Sync.class)实现了全局锁的效果

//synchronized锁这个类对应的class对象
class MyThread extends Thread{
    @Override
    public void run() {
        Sync sync = new Sync();
        sync.test();
    }
}
class Sync{
    public void test(){
        synchronized(Sync.class){
            System.out.println("test方法开始,当前线程"+Thread.currentThread().getName());
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("test方法结束,当前线程"+Thread.currentThread().getName());
        }
    }
}
public class Test {
    public static void main(String[] args) {
        for (int i = 0; i < 3; i++){
            Thread thread = new MyThread();
            thread.start();
        }
    }
}

static synchronized()方法,static方法可以直接通过类名加方法名调用,方法中无法使用this,所有他锁的不是this而是类的class对象。
所以,static synchronized方法也相当于全局锁,相当于锁住了代码段

3、JDK1.5提供的Lock锁

使用ReentrantLock进行同步处理

class MyThread implements Runnable {
    private int ticket = 50;
    private Lock ticketLock = new ReentrantLock() ;
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            ticketLock.lock();
            try {
                if (this.ticket > 0) { // 还有票
                    try {
                        Thread.sleep(20);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } // 模拟网络延迟
                    System.out.println(Thread.currentThread().getName() + ",还有" + this.ticket--+" 张票");
                }
            } finally {
                ticketLock.unlock();
            }
        }
    }
}
public class Test{
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread thread1 = new Thread(myThread,"线程1");
        Thread thread2 = new Thread(myThread,"线程2");
        Thread thread3 = new Thread(myThread,"线程3");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

在JDK1.5中,synchronized的性能是比较低效的。因为他是一个重量级操作,挂起线程和恢复线程的操作都需要转入内核态中完成,这些操作给系统的并发性带来了很大的压力。
相比之下,使用Java提供的Lock对象,性能会更高一点。

猜你喜欢

转载自blog.csdn.net/ly52014/article/details/88606561