线程同步

为什么是线程同步,什么是线程同步

多个线程访问公共资源时,只有一个独占!否则资源出问题

一个简单的同步情景


public class SynchronizedTest implements Runnable{
    Timer timer = new Timer();
    public static void main(String[] args) {
        SynchronizedTest test = new SynchronizedTest();
        Thread aThread = new Thread(test);
        Thread bThread = new Thread(test);
        aThread.setName("a");
        bThread.setName("b");
        aThread.start();
        bThread.start();
    }
    
    public void run(){
        timer.add(Thread.currentThread().getName());
    }
}


class Timer{
    private static int num = 0;
    
    public void add(String threadName){
        num ++;
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return ;
        }
        System.out.println(threadName+" "+num);
    }
}
//a 2
//b 2

第一个线程访问时,静态变量num++ 变为1,第一个线程此时还未输出,但是另一个线程此时也访问,静态变量为2,最终输出结果如上;
而我们现在想得到的结果是一个原子操作,一个线程得到的num:1,输出;另一个线程得到的num:2,输出

同步锁


public class SynchronizedTest implements Runnable{
    Timer timer = new Timer();
    public static void main(String[] args) {
        SynchronizedTest test = new SynchronizedTest();
        Thread aThread = new Thread(test);
        Thread bThread = new Thread(test);
        aThread.setName("a");
        bThread.setName("b");
        aThread.start();
        bThread.start();
    }
    
    public void run(){
        timer.add(Thread.currentThread().getName());
    }
}


class Timer{
    private static int num = 0;
    
    public synchronized void add(String threadName){
        //synchronized (this) {
            num ++;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                return ;
            }
            System.out.println(threadName+" "+num);
        //}
    }
}

死锁

一个简单死锁的例子


public class DeadLock implements Runnable{
    public static Object o1 = new Object();
    public static Object o2 = new Object();
    public int flag = -1;
    public static void main(String args[]) {
        // TODO Auto-generated method stub
        DeadLock deadLock1 = new DeadLock();
        DeadLock deadLock2 = new DeadLock();

        deadLock1.flag = 1;
        deadLock2.flag = 0;
        Thread aThread = new Thread(deadLock1);
        Thread bThread = new Thread(deadLock2);
        
        aThread.start();bThread.start();
    }

    @Override
    public void run() {
        System.out.println(this);
        // TODO Auto-generated method stub
        if(flag == 1){
            synchronized (o1) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                synchronized (o2){
                    System.out.println(flag+" o1");
                }
                
            }
        }
        
        if(flag == 0){
            synchronized (o2) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                synchronized (o1){
                    System.out.println(flag+" o2");
                }
                
            }
        }
    }
}
//DeadLock@782bbb7b
DeadLock@7f21c5df
ing...

为什么会死锁

非同步的方法依旧可以给其他线程执行


public class OrtherTest implements Runnable{
    int b = 1000;
    public static void main(String[] args) {
        OrtherTest test = new OrtherTest();
        Thread thread = new Thread(test);
        thread.start();
        
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        test.method();
        
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        synchronized (this) {
            b = 2000;
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(b + "run()");
        }
    }
    
    public synchronized void method() {
        b = 3000;
        System.out.println(b +" method()");
    }
    
    
}
//3000 method()
//3000run()
开启的分支线程先拿到锁,b = 2000,但未输出;主线程里执行一个普通方法b = 3000,先行输出(说明普通方法可以直接执行)

一个任务可以多次获得对象的锁

/** 
 * 同一任务可以再次持有对象锁 create on 2010.08.04 08:27 
 *  
 * @since jdk1.6 
 * @author maozj 
 * @version 1.0 
 *  
 */  
public class MutiObjectLock {  
    /** 
     * Test client 
     * @param args 
     */  
    public static void main(String[] args) {  
        new Thread() {  
            public void run() {  
                new MutiObjectLock().f1();  
            }  
        }.start();  
        new Thread() {  
            public void run() {  
                new MutiObjectLock().f3();  
            }  
        }.start();  
    }  
  
    private static int count = 10;  
  
    /** 
     * synchronized f1() 
     */  
    public synchronized void f1() {  
        if (--count > 0) {  
            System.out.println("f1() calling f2() count " + count);  
            f2();  
        }  
    }  
  
    /** 
     * synchronized f2() 
     */  
    public synchronized void f2() {  
        if (--count > 0) {  
            System.out.println("f2() calling f1() count " + count);  
            f1();  
        }  
    }  
    
    public synchronized void f3() {   
            System.out.println("f3() calling  count " + count);  
    }  
}  
/*
f1() calling f2() count 9
f2() calling f1() count 8
f1() calling f2() count 7
f2() calling f1() count 6
f1() calling f2() count 5
f2() calling f1() count 4
f1() calling f2() count 3
f2() calling f1() count 2
f1() calling f2() count 1
f3() calling  count 0

*/

如果一个方法在一个同一个对象上调用了第二个方法,同时第二个方法也调用了同一对象上的另一方法,JVM就会发生这样的情况。JVM负责跟踪对象被加锁的次数,锁被完全释放,引用计数为0,每当这个任务在这个对象上获得锁时(+1),每当任务离开synchronize(-1),例如(synchronize(this)任务获得this对象锁),注意都是同一对象

wait、sleep、notify

wait():某一条件下,任务阻塞,让出锁的占有;
sleep():当前线程阻塞,但依旧占有锁的权利;
notify():唤醒众多等待同一个锁的任务中只有一个会被唤醒,当前还占有锁的任务继续执行完到synchronize代码块结束,之后让出锁的占有

生产者消费者

wait、notify实现

public class ProducerAndConsumer {  
    public static void main(String[] args) {  
        ProductList pl = new ProductList();  
        Factory f = new Factory(pl);  
        Consumer c = new Consumer(pl);  
  
        Thread t1 = new Thread(f);  
        Thread t2 = new Thread(c);  
  
        t1.start();  
        t2.start();  
  
    }  
}  
  
class Product {  
    private int id;  
  
    Product(int id) {  
        this.id = id;  
    }  
  
    @Override  
    public String toString() {  
        return "Product [id=" + id + "]";  
    }  
  
}  
  
class ProductList {  
    int index = 0;  
    private Product[] p = new Product[6];  
  
    public synchronized void push(Product pr) {  
  
        while (index == p.length) {  
            try {  
                this.wait();  
            } catch (InterruptedException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
        }  
        this.notify();  
        p[index] = pr;  
        System.out.println("生产了" + p[index]);
        index++;  
  
    }  
  
    public synchronized Product pop() {  
  
        while (index == 0) {  
            try {  
                this.wait();  
            } catch (InterruptedException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
        }  
        this.notify();  
        index--;  
        System.out.println("消费了" + p[index]);
        return p[index];  
  
    }  
}  
  
class Factory implements Runnable {  
  
    private ProductList pl = null;  
  
    Factory(ProductList pl) {  
        this.pl = pl;  
    }  
  
    @Override  
    public void run() {  
        // TODO Auto-generated method stub  
        for (int i = 0; i < 20; i++) {  
            Product p = new Product(i);  
            pl.push(p);  
            try {  
                Thread.sleep(1000);  
            } catch (InterruptedException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
        }  
  
    }  
}  
  
class Consumer implements Runnable {  
    private ProductList pl = null;  
  
    Consumer(ProductList pl) {  
        this.pl = pl;  
    }  
  
    @Override  
    public void run() {  
        // TODO Auto-generated method stub  
        for (int i = 0; i < 20; i++) {  
            Product p = pl.pop();  
            try {  
                Thread.sleep(2000);  
            } catch (InterruptedException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
        }  
    }  
}  

之前打印语句在pop、push同步操作之后执行,这样会导致错误的结果:消费者先行消费了0号产品,生产者后生产的结果,但实际队列存储的数据是正确的。因为run()里的输出语句此时是多线程同时运行,无法控制先后。
//Todo 其他实现方式

你可能感兴趣的:(线程同步)