多线程知识点:同步

/*
两个储户,到同一个银行存钱,每个人存了3次,一次100元。
1,描述银行。
2,描述储户任务。

分析多线程是否存在安全隐患。

1,线程任务中是否有共享的数据。
2,是否多条操作共享数据的代码。



同步函数。其实就是在函数上加上了同步关键字进行修饰。
同步表现形式有两种:1,同步代码块,2,同步函数。

同步函数使用的锁是什么呢?函数需要被对象调用,哪个对象不确定,但是都用this来表示。
同步函数使用的锁就是this。

*/
class Bank
{
    private int sum;
//    private Object obj = new Object();
    public synchronized  void add(int n)
    {
//        synchronized(obj)
//        {
        sum = sum + n;
        try{Thread.sleep(10);}catch(Exception e){}
        System.out.println("sum="+sum);
//        }
        
    }
}
class Customer implements Runnable
{
    private    Bank b = new Bank();
    public void run()
    {
        for(int x=0; x<3; x++)
        {
            b.add(100);
        }
    }
}

class BankDemo
{
    public static void main(String[] args) 
    {
        //1,创建任务对象。
        Customer c = new Customer();
        Thread t1 = new Thread(c);
        Thread t2 = new Thread(c);
        t1.start();
        t2.start();
    }
}
/*
验证同步函数使用的锁是this。

验证需求:
启动两个线程。
一个线程负责执行同步代码块(使用明锁)。
另一个线程使用同步函数(使用this锁)。
两个执行的任务是一样的都是卖票。如果他们没有使用相同的锁,说明他们没有同步。会出现数据错误。

怎么能让一个线程一直在同步代码块中,一个线程在同步函数呢?
可以通过切换的方式。
*/

class SaleTicket implements Runnable
{
    private int tickets = 100;
    //定义一个boolean标记。
    boolean flag = true;
    Object obj = new Object();
    public void run()
    {
        if(flag)        
            while(true)
            {
                synchronized(this)
                {
                    if(tickets>0)
                    {    
                        try{Thread.sleep(10);}catch(InterruptedException e){}//让线程到这里稍微停一下。
                        System.out.println(Thread.currentThread().getName()+"....code....."+tickets--);
                    }
                }
            }
        else
            while(true)
                sale();
    }

    public synchronized void sale()
    {
        if(tickets>0)
        {    
            try{Thread.sleep(10);}catch(InterruptedException e){}//让线程到这里稍微停一下。
            System.out.println(Thread.currentThread().getName()+".....func...."+tickets--);
        }
    }
}



class ThisLockDemo 
{
    public static void main(String[] args) throws InterruptedException
    {
        SaleTicket t = new SaleTicket();

        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        t1.start();
        Thread.sleep(10);
        t.flag = false;
        t2.start();
    }
}
/*
如果同步函数被static修饰呢?

static方法随着类加载,这时不一定有该类的对象。但是一定有一个该类的字节码文件对象。
这个对象简单的表示方式就是 类名.class  Class



*/

class SaleTicket implements Runnable
{
    private static int tickets = 100;
    //定义一个boolean标记。
    boolean flag = true;
    Object obj = new Object();
    public void run()
    {
        if(flag)        
            while(true)
            {
                synchronized(SaleTicket.class)
                {
                    if(tickets>0)
                    {    
                        try{Thread.sleep(10);}catch(InterruptedException e){}//让线程到这里稍微停一下。
                        System.out.println(Thread.currentThread().getName()+"....code....."+tickets--);
                    }
                }
            }
        else
            while(true)
                sale();
    }

    public static  synchronized void sale()
    {
        if(tickets>0)
        {    
            try{Thread.sleep(10);}catch(InterruptedException e){}//让线程到这里稍微停一下。
            System.out.println(Thread.currentThread().getName()+".....func...."+tickets--);
        }
    }
}



class StaticLockDemo 
{
    public static void main(String[] args) throws InterruptedException
    {
        SaleTicket t = new SaleTicket();

        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        t1.start();
        Thread.sleep(10);
        t.flag = false;
        t2.start();
    }
}
//饿汉式。相对与多线程并发,安全!

class Single
{
    private static final Single SINGLE_INTSTANCE = new Single();
    private Single(){}
    public static Single getInstance()
    {
        return SINGLE_INTSTANCE;
    }
}

//懒汉式。延迟加载模式。
/*
在多线程并发访问时,会出现线程安全问题。
加了同步就可以解决问题。无论是同步函数,还是同步代码块都行。
但是,效率低了。
怎么解决效率低的问题。
可以通过if对单例对象的双重判断的形式。哦耶!


*/
class Single
{
    private static Single s = null;
    private Single(){}
    public static  Single getInstance()
    {
        if(s==null)
        {
            synchronized(Single.class)
            {
                if(s==null)
                    //-->0 
                    s = new Single();
            }
        }
        return s;
    }
}

class Demo implements Runnable
{
    public void run()
    {
        Single.getInstance();
    }
}


class ThreadSingleTest 
{
    public static void main(String[] args) 
    {
        System.out.println("Hello World!");
    }
}
/*
同步函数,同步代码块。

同步代码块使用的任意的对象作为锁。
同步函数只能使用this作为锁。


如果说:一个类中只需要一个锁,这时可以考虑同步函数,使用this,写法简单。
但是,一个类中如果需要多个锁,还有多个类中使用同一个锁,这时只能使用同步代码块。

建议使用同步代码块。

*/

class  
{
    public static void main(String[] args) 
    {
        System.out.println("Hello World!");
    }
}
/*
死锁:

场景一:

同步嵌套。



*/

class SaleTicket implements Runnable
{
    private int tickets = 100;
    //定义一个boolean标记。
    boolean flag = true;
    Object obj = new Object();
    public void run()
    {
        if(flag)        
            while(true)
            {
                synchronized(obj)//obj lock
                {
                    sale();
                }
            }
        else
            while(true)
                sale();
    }

    public synchronized void sale()//this lock
    {
        synchronized(obj)
        {
            if(tickets>0)
            {    
                try{Thread.sleep(10);}catch(InterruptedException e){}//让线程到这里稍微停一下。
                System.out.println(Thread.currentThread().getName()+".....func...."+tickets--);
            }
        }
    }
}



class DeadLockDemo 
{
    public static void main(String[] args) throws InterruptedException
    {
        SaleTicket t = new SaleTicket();

        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        t1.start();
        Thread.sleep(10);
        t.flag = false;
        t2.start();
    }
}
/*
多线程间的通信。多个线程都在处理同一个资源,但是处理的任务却不一样。
生产者,消费者。

通过同步,解决了没生产就消费的问题。


*/
//描述资源。
class Res
{
    private String name;
    private int count = 1;

    //提供了给商品赋值的方法。
    public synchronized void set(String name)
    {
        this.name  = name + "--" + count;

        count++;

        System.out.println(Thread.currentThread().getName()+"...生产者....."+this.name);
    }

    //提供一个获取商品的方法。
    public synchronized void get()
    {
        System.out.println(Thread.currentThread().getName()+".......消费者....."+this.name);
    }
}


//生成者。
class Producer implements Runnable
{
    private Res r;
    Producer(Res r)
    {
        this.r = r;
    }
    public void run()
    {
        while(true)
            r.set("面包");
    }
}

//消费者
class Consumer implements Runnable
{
    private Res r;
    Consumer(Res r)
    {
        this.r = r;
    }
    public void run()
    {    
        while(true)
        r.get();
    }
}




class  ProducerConsumerDemo
{
    public static void main(String[] args) 
    {
        //1,创建资源。
        Res r = new Res();
        //2,创建两个任务。
        Producer pro = new Producer(r);
        Consumer con = new Consumer(r);

        //3,创建线程。
        Thread t1 = new Thread(pro);
        Thread t2 = new Thread(con);

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

//同步嵌套,死锁。

class Task implements Runnable
{
    private boolean flag;
    Task(boolean flag)
    {
        this.flag = flag;
    }
    public void run()
    {
        if(flag)
        {
            while(true)
                synchronized(MyLock.LOCKA)
                {
                    System.out.println("if.....locka");
                    synchronized(MyLock.LOCKB)
                    {
                        System.out.println("if.....lockb");
                    }
                }
        }
        else
        {
            while(true)
                synchronized(MyLock.LOCKB)
                {
                    System.out.println("else.....lockb");
                    synchronized(MyLock.LOCKA)
                    {
                        System.out.println("else.....locka");
                        
                    }
                }
        }
    }
}

class MyLock
{
    public static final Object LOCKA = new Object();
    public static final Object LOCKB = new Object();
}




class DeadLockTest 
{
    public static void main(String[] args) 
    {
        //创建线程任务。
        Task t1 = new Task(true);
        Task t2 = new Task(false);
        new Thread(t1).start();
        new Thread(t2).start();
    }
}
/*
多线程间的通信。多个线程都在处理同一个资源,但是处理的任务却不一样。
生产者,消费者。

通过同步,解决了没生产就消费的问题。

但是出现了连续的生产没有消费的情况,和需求生产一个,消费一个的情况不符。

使用了等待唤醒机制。
wait():该方法可以让线程处于冻结状态,并将线程临时存储到线程池中。
notify():唤醒指定线程池中的任意一个线程。
notifyAll():唤醒指定线程池中的所以线程。



这些方法必须使用在同步中,因为它们用来操作同步锁上的线程的状态的。

在使用这些方法时,必须标识它们所属于的锁。标识方式就是 锁对象.wait();  锁对象.notify();  锁对象.notifyAll();

相同锁的notify(),可以获取相同锁的wait();



*/
//描述资源。
class Res
{
    private String name;
    private int count = 1;

    //定义标记。
    private boolean flag;
    //提供了给商品赋值的方法。
    public synchronized void set(String name)
    {
        if(flag)//判断标记为true,执行wait。等待。为false。就生产。
            try{this.wait();}catch(InterruptedException e){}
        this.name  = name + "--" + count;

        count++;

        System.out.println(Thread.currentThread().getName()+"...生产者....."+this.name);
        //生成完毕,将标记改为true。
        flag = true;

        //唤醒消费者。
        this.notify();
    }

    //提供一个获取商品的方法。
    public synchronized void get()
    {
        if(!flag)
            try{this.wait();}catch(InterruptedException e){}
        System.out.println(Thread.currentThread().getName()+".......消费者....."+this.name);

        //将标记改为false。
        flag = false;
        //唤醒生产者。
        this.notify();
    }
}


//生成者。
class Producer implements Runnable
{
    private Res r;
    Producer(Res r)
    {
        this.r = r;
    }
    public void run()
    {
        while(true)
            r.set("面包");
    }
}

//消费者
class Consumer implements Runnable
{
    private Res r;
    Consumer(Res r)
    {
        this.r = r;
    }
    public void run()
    {    
        while(true)
            r.get();
    }
}




class  ProducerConsumerDemo2
{
    public static void main(String[] args) 
    {
        //1,创建资源。
        Res r = new Res();
        //2,创建两个任务。
        Producer pro = new Producer(r);
        Consumer con = new Consumer(r);

        //3,创建线程。
        Thread t1 = new Thread(pro);
        Thread t2 = new Thread(con);

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

多生产多消费。

问题1:
    重复生成,重复消费。
    原因:经过复杂的(等,资格)分析,发现被唤醒的线程没有判断标记就开始工作(生成or消费)了。
    导致了重复的生成和消费的发生。

    解决:那就是被唤醒的线程必须判断标记。
        使用while循环搞定。



问题2:
    死锁了。所有的线程都处于冻结状态。
    原因:本方线程在唤醒时,又一次唤醒了本方线程。而本方线程循环判断标记,又继续等待。
    而导致所有的线程都等待了。

    解决;希望本方如果唤醒了对方线程,就可以解决。
        可以使用notifyAll()方法。
        那不是全唤醒了吗?是的。既有本方,又有对方。但是本方醒后,会判断标记继续等待。
        这样对方就有线程可以执行了。



已经实现了多生产多消费。

但是有些小问题,效率有点低,因为notifyAll也唤醒了本方。做了不必要的判断。
*/


//描述资源。
class Res
{
    private String name;
    private int count = 1;

    //定义标记。
    private boolean flag;
    //提供了给商品赋值的方法。
    public synchronized void set(String name)//
    {
        while(flag)//判断标记为true,执行wait。等待。为false。就生产。
            try{this.wait();}catch(InterruptedException e){}//t0(等)  t1(等)
        this.name  = name + "--" + count;//面包1  ,面包2 面包3

        count++;//2  3 4

        System.out.println(Thread.currentThread().getName()+"...生产者....."+this.name);//t0  面包1、 t0 面包2 t1 ,面包3
        //生成完毕,将标记改为true。
        flag = true;

        //唤醒所有等待线程(包括本方线程)。
        this.notifyAll();
    }

    //提供一个获取商品的方法。
    public synchronized void get()//
    {
        while(!flag)
            try{this.wait();}catch(InterruptedException e){}//t2(等)  t3(等)
        System.out.println(Thread.currentThread().getName()+".......消费者....."+this.name);//t2  面包1.

        //将标记改为false。
        flag = false;
        //唤醒所有等待线程(包括本方线程)。
        this.notifyAll();
    }
}


//生成者。
class Producer implements Runnable
{
    private Res r;
    Producer(Res r)
    {
        this.r = r;
    }
    public void run()
    {
        while(true)
            r.set("面包");
    }
}

//消费者
class Consumer implements Runnable
{
    private Res r;
    Consumer(Res r)
    {
        this.r = r;
    }
    public void run()
    {    
        while(true)
            r.get();
    }
}




class  ProducerConsumerDemo3
{
    public static void main(String[] args) 
    {
        //1,创建资源。
        Res r = new Res();
        //2,创建两个任务。
        Producer pro = new Producer(r);
        Consumer con = new Consumer(r);

        //3,创建线程。
        Thread t0 = new Thread(pro);
        Thread t1 = new Thread(pro);
        Thread t2 = new Thread(con);
        Thread t3 = new Thread(con);

        t0.start();
        t1.start();
        t2.start();
        t3.start();
    }
}
/*

1,搞定妖的问题。


2,name和sex是私有的。需要在Res类中对外提供访问name和sex的方法。这个可以参照生产者消费者producerconsumerdemo.java


3,实现间隔输出,使用等待唤醒机制。producerconsumerdemo2.java



*/

class Res
{
    String name;
    String sex;
}

class Input implements Runnable
{

    private Res r;
    Input(Res r)
    {
        this.r = r;
    }
    public void run()
    {
        int x = 0;
        while(true)
        {
            if(x==0)
            {
                r.name = "张三";
                r.sex = "男男男男男男男";
            }
            else
            {
                r.name = "rose";
                r.sex = "women";
            }
            x = (x+1)%2;
        }
    
    }

}

class Output implements Runnable
{

    private Res r;
    Output(Res r)
    {
        this.r = r;
    }
    public void run()
    {
        while(true)
        {
            System.out.println(r.name+"...."+r.sex);
        }
    }

}


class Test 
{
    public static void main(String[] args) 
    {
        Res r = new Res();

        Input in = new Input(r);
        Output out = new Output(r);

        new Thread(in).start();
        new Thread(out).start();
    
    
    }
}
原文地址:https://www.cnblogs.com/vijay/p/3511261.html