Java 线程同步

synchronized关键字:执行方法时,锁定当前对象,保证整个操作过程不能被打断

public class TestSync implements Runnable {
    Timer timer = new Timer();
    
    public static void main(String[] args) {
        TestSync test = new TestSync();

        Thread t1 = new Thread(test);
        Thread t2 = new Thread(test);
        t1.setName("T1");
        t2.setName("T2");

        t1.start();
        t2.start();
    }

    public void run() {
        timer.add(Thread.currentThread().getName());
    }
}

class Timer {
    public static int num = 0;
    public /*synchronized*/ void add(String name) {
           //使用方法1
        synchronized (this) {
            //使用关键字,执行方法过程中锁定当前对象(互斥锁)
            //使用方法2
            
            num++;  
            try {
                Thread.sleep(1);
                //第一个线程睡眠时,第二个线程已经将num更改为2
                //导致结果都为"你是第2个使用Timer的线程"
                //原因:第一个线程执行过程中被第二个线程打断(sleep是为了放大效果)
            } catch (InterruptedException ex) {
            
            }
            System.out.println(name + ": 你是第" + num + "个使用Timer的线程");
        }
    }
}

死锁:

    原理:某个任务在等待另一个任务,而后者又在等待第一个任务释放锁,导致一个任务之间互相等待的连续循环

    举例:A需要先访问x,再访问y;B需要先访问y,再访问x。当两个线程执行时,A对x加了锁,B对y加了锁,然后A想继续执行则需要访问y(即需要等待B线程执行完毕),而B想要继续执行则需要访问x(即需要等待A线程执行完毕)。导致A、B两个线程进入互相等待的状态,即阻塞状态,都无法继续执行。


解决方法之一:加粗锁的粒度(对大对象加锁,而不是对对象里的小对象加锁)

除非实现系统级的程序(例如数据库产品),一般不会遇到死锁


面试例题:

    非加锁方法仍然可以被访问

public class TT implements Runnable{
    int b = 100;

    public synchronized void m1() throws Exception {
        //m1方法被锁定,但m2并未被锁定,所以可以访问
        b = 1000;
        Thread.sleep(5000);
        System.out.println("b = " + b);
        
    }

    public void m2() {
        System.out.println(b);
    }

    public void run() {
        try {
            m1();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) throws Exception {
        TT tt = new TT();
        Thread t = new Thread(tt);

        t.start();
        Thread.sleep(1000);
        
        //m1被锁定,m2仍然可以访问
        tt.m2();
        /*输出结果为:
          1000
          b = 1000
        */
        
    }
}

生产者消费者问题:基础版本

主要涉及到wait和notify方法的使用

public class ProducerConsumer {
    public static void main(String[] args) {
        
        SyncStack ss = new SyncStack();
        Producer p = new Producer(ss);
        Consumer c = new Consumer(ss);

        new Thread(p).start();
        new Thread(c).start();
        
    }
}

class Product {
    int id;
    Product(int id) {
        this.id = id;
    }

    public String toString() {
        return "Product: " + id; 
    }
}

class SyncStack {
    int index = 0;
    Product[] arrProduct = new Product[6];

    public synchronized void push(Product p) {
        while(index == arrProduct.length) {
            //使用while,而不是if,确保出现exception后,仍然会检查index
            try {
                this.wait();
                //访问当前对象的线程等待
                //wait过程 中,锁不在归当前线程所有
                
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }

        this.notifyAll();
        //叫醒在该对象上等待的线程
        
        arrProduct[index] = p;
        index++;
    }

    public synchronized Product pop() {
        while(index == 0) {
            try {
                this.wait();
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }

        this.notifyAll();
        
        index--;
        return arrProduct[index];
    }
}

class Producer implements Runnable {
    SyncStack ss = null;

    Producer(SyncStack ss) {
        this.ss = ss;
    }

    public void run() {
        for(int i = 0; i < 20; i++) {
            Product p = new Product(i);
            ss.push(p);
            System.out.println("生产: " + p + " 剩余: " + ss.index);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Consumer implements Runnable {
    SyncStack ss = null;

    Consumer(SyncStack ss) {
        this.ss = ss;
    }

    public void run() {
        for(int i = 0; i < 20; i++) {
            Product p = ss.pop();
            System.out.println("消费: " + p + " 剩余: " + ss.index);
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

wait和sleep区别:

wait:调用时必须锁定该对象,别的线程可以访问锁定对象

sleep:别的线程不可以访问锁定对象


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值