synchronized互斥锁实例解析


synchronized是Java提供的内置锁,里边有类锁和对象锁;

在静态方法中,我们一般使用类锁,在实例方法中,我们一般使用对象锁

sleep是带锁沉睡,sleep方法是在阻塞当前线程继续执行的同时,并持有该对象锁,所以该对象的其他同步线程是无法执行的,不是同一个对象的同步线程是可以执行的

synchronized互斥锁实例解析

1、互斥锁基础使用:防止多个线程同时访问对象的synchronized方法。

只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。

1.1、多个线程调用同一个方法

1.1.1、不加互斥锁时

多个线程,cpu时间片切换的时候,会出现执行到一部分,切换到其它线程的情况,导致输出混乱。

public class MyThread {

    public static void main(String[] args) {
        MyThread s = new MyThread();
        s.thread();

    }

    public void thread() {

        final Printer p = new Printer();
        new Thread(new Runnable() {

            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(10);
                        p.print("1111111111");
                    } catch (Exception e) {

                    }
                }
            }
        }, "t1").start();

        new Thread(new Runnable() {

            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(10);
                        p.print("2222222222");
                    } catch (Exception e) {

                    }

                }
            }
        }, "t2").start();
    }


    class Printer {
        //synchronized
        public void print(String str) {
//			synchronized(this){
            for (int i = 0; i < str.length(); i++) {
                System.out.print(str.charAt(i));
            }
            System.out.println();
            System.out.println("上面执行的是线程:" + Thread.currentThread().getName());
//		}
        }
    }
}

输出结果片断:

1111111111
上面执行的是线程:t1
2222222222
上面执行的是线程:t2
1111111111
上面执行的是线程:t1
2222222222
上面执行的是线程:t2
11122222221111111222

上面执行的是线程:t2
上面执行的是线程:t1
2111222222222
上面执行的是线程:t2
1111111
上面执行的是线程:t1
2222222222
上面执行的是线程:t2
1111111111
上面执行的是线程:t1
12111111111222222222

上面执行的是线程:t2
上面执行的是线程:t1
1111111111
上面执行的是线程:t1
2222222222
上面执行的是线程:t2

1.1.2、加互斥锁后

package cn.itcast_01_mythread.thread.thread;

/*
 * 线程安全问题
 */
public class MyThread {

	public static void main(String[] args) {
		MyThread s=new MyThread();
		s.thread();

	}

	public void thread(){

		final Printer p=new Printer();
		new Thread(new Runnable() {

			@Override
			public void run() {
				while(true){
				try{
					Thread.sleep(10);
					p.print("1111111111");
				}catch(Exception e){

				}
				}
			}
		},"t1").start();

new Thread(new Runnable() {

			@Override
			public void run() {
				while(true){
				try{
					Thread.sleep(10);
					p.print("2222222222");
				}catch(Exception e){

				}

			}
			}
		},"t2").start();
	}


	class Printer{
		//synchronized
		public  synchronized void print(String str){
//			synchronized(this){
			for(int i=0;i<str.length();i++){
				System.out.print(str.charAt(i));
			}
			System.out.println();
			System.out.println("上面执行的是线程:"+Thread.currentThread().getName());
//		}
		}
		}
	}


输出结果:不会出现错位位置

2222222222
上面执行的是线程:t2
1111111111
上面执行的是线程:t1
2222222222
上面执行的是线程:t2
1111111111
上面执行的是线程:t1
2222222222
上面执行的是线程:t2
1111111111
上面执行的是线程:t1
2222222222
上面执行的是线程:t2
1111111111
上面执行的是线程:t1
2222222222
上面执行的是线程:t2
1111111111

1.2、多个线程多个锁,升级为类锁

1.2.1、未升级成类锁前

package cn.itcast_01_mythread.thread.thread;

/**
 * 多个线程多个锁
 * @author HL
 *
 */
public class MyThread2 {
	private static int age=0;
	
	public static void main(String[] args) {
		final MyThread2 m1=new MyThread2();
		final MyThread2 m2=new MyThread2();
		Thread t1=new Thread(new Runnable() {
			@Override
			public void run() {
				m1.printNum("zs");
			}
		});
		
		Thread t2=new Thread(new Runnable() {
			
			@Override
			public void run() {
				m2.printNum("lisi");
				
			}
		});
		
		t1.start();
		t2.start();
	}

	public synchronized void printNum(String name){
			try {
				if(name.equals("zs")){
					age=18;
					System.out.println("当前调用printNum线程是"+Thread.currentThread().getName());
					System.out.println("thread zs do something");
					Thread.sleep(1000);
				}else{
					age=20;
					System.out.println("当前调用printNum线程是"+Thread.currentThread().getName());
					System.out.println("thread lisi do something");
				}
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		System.out.println("当前调用printNum线程是"+Thread.currentThread().getName());
		System.out.println("name="+name+"----age="+age);
	}
}

输出结果:

当前调用printNum线程是Thread-0
thread zs do something
当前调用printNum线程是Thread-1
thread lisi do something
当前调用printNum线程是Thread-1
name=lisi----age=20
当前调用printNum线程是Thread-0
name=zs----age=20

2.2.2、加上static关键字,升级为类锁后

当调用setValue方法的时候,执行到sleep方法,sleep是带锁沉睡。sleep在阻塞当前线程(Thread-0)的同时,并持有该对象锁(对象Dirty_read_4 dr),所以该对象的其他同步线程无法执行。

而getValue并没有加锁,所以当Thread-0沉睡时,主线程往下执行,调用getValue方法。

package cn.itcast_01_mythread.thread.thread;

/**
 * 多个线程多个锁
 * @author HL
 *
 */
public class MyThread2 {
	private static int age=0;
	
	public static void main(String[] args) {
		final MyThread2 m1=new MyThread2();
		final MyThread2 m2=new MyThread2();
		Thread t1=new Thread(new Runnable() {
			@Override
			public void run() {
				m1.printNum("zs");
			}
		});
		
		Thread t2=new Thread(new Runnable() {
			
			@Override
			public void run() {
				m2.printNum("lisi");
				
			}
		});
		
		t1.start();
		t2.start();
	}
	//TODO static 升级成class
	public static synchronized void printNum(String name){
			try {
				if(name.equals("zs")){
					age=18;
					System.out.println("当前调用printNum线程是"+Thread.currentThread().getName());
					System.out.println("thread zs do something");
					Thread.sleep(1000);
				}else{
					age=20;
					System.out.println("当前调用printNum线程是"+Thread.currentThread().getName());
					System.out.println("thread lisi do something");
				}
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		System.out.println("当前调用printNum线程是"+Thread.currentThread().getName());
		System.out.println("name="+name+"----age="+age);
	}
}

输出结果:

当前调用printNum线程是Thread-0
thread zs do something
当前调用printNum线程是Thread-0
name=zs----age=18
当前调用printNum线程是Thread-1
thread lisi do something
当前调用printNum线程是Thread-1
name=lisi----age=20

2、线程的同步异步问题(多个线程)

2.1、未加前

package cn.itcast_01_mythread.thread.thread;
/*
 * 线程的同步异步
 */
public class Sync_Async_3 {
    public synchronized void method1(){
        try{
            System.out.println(Thread.currentThread().getName());
            Thread.sleep(4000);
            System.out.println("线程0-------执行完毕");
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    public void method2(){
        try{
            System.out.println(Thread.currentThread().getName());
            System.out.println("线程1--------执行完毕");
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        final Sync_Async_3 sa = new Sync_Async_3() ;
        /**
         * T1线程先持有Sync_Async对象的Lock锁 , t2线程可以以异步的方式调用对象中非synchronized修饰的方法
         * 如果在method2上加入synchronized关键字,t2这个时候如果想调用synchronized关键字的方法,就需要等t1执行完毕之后才能调用,也就是同步
         * */

        Thread t1 = new Thread(new Runnable() {
            public void run() {
                sa.method1();
            }
        }) ;

        Thread t2 = new Thread(new Runnable() {
            public void run() {
                sa.method2();
            }
        }) ;
        t1.start();
        t2.start();
    }
}

输出结果:

Thread-0
Thread-1
线程1--------执行完毕
线程0-------执行完毕

2.2、加锁之后,同步

package cn.itcast_01_mythread.thread.thread;
/*
 * 线程的同步异步
 */
public class Sync_Async_3 {
    public synchronized void method1(){
        try{
            System.out.println(Thread.currentThread().getName());
            Thread.sleep(4000);
            System.out.println("线程0-------执行完毕");
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    //TODO synchronized
    public  synchronized void method2(){
        try{
            System.out.println(Thread.currentThread().getName());
            System.out.println("线程1--------执行完毕");
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        final Sync_Async_3 sa = new Sync_Async_3() ;
        /**
         * T1线程先持有Sync_Async对象的Lock锁 , t2线程可以以异步的方式调用对象中非synchronized修饰的方法
         * 如果在method2上加入synchronized关键字,t2这个时候如果想调用synchronized关键字的方法,就需要等t1执行完毕之后才能调用,也就是同步
         * */

        Thread t1 = new Thread(new Runnable() {
            public void run() {
                sa.method1();
            }
        }) ;

        Thread t2 = new Thread(new Runnable() {
            public void run() {
                sa.method2();
            }
        }) ;
        t1.start();
        t2.start();
    }
}

输出结果:

Thread-0
线程0-------执行完毕
Thread-1
线程1--------执行完毕

3、脏读

3.1、不加锁(一个线程)

当调用setValue方法的时候,执行到sleep方法,sleep是带锁沉睡。sleep在阻塞当前线程(Thread-0)的同时,并持有该对象锁(对象Dirty_read_4 dr),所以该对象的其他同步线程无法执行。

而getValue并没有加锁,所以当Thread-0沉睡时,主线程往下执行,调用getValue方法。

package cn.itcast_01_mythread.thread.thread;
/*
 * 脏读
 */
public class Dirty_read_4 {
    private String weibo_name = "angel" ;
    private String weibo_sex = "女" ;
    //synchronized
    public synchronized void setValue(String weibo_name , String weibo_sex){
        System.out.println("执行setValue方法的线程是:"+Thread.currentThread().getName());
        this.weibo_name = weibo_name ;
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.weibo_sex = weibo_sex ;
        System.out.println("setValue---------weibo_name :" + weibo_name + "-- weibo_sex : "+weibo_sex);
    }
    public  void getValue(){
        System.out.println("执行getValue方法的线程是:"+Thread.currentThread().getName());
        System.out.println("getValue---------weibo_name :" + weibo_name + "-- weibo_sex : "+weibo_sex);
    }

    public static void main(String[] args) throws InterruptedException {
        final Dirty_read_4 dr = new Dirty_read_4();
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                dr.setValue("huangxiaoming" , "男");
            }
        }) ;
        t1.start();
        Thread.sleep(1000);
        dr.getValue();
    }
}

输出结果:顺序混乱

执行setValue方法的线程是:Thread-0
执行getValue方法的线程是:main
getValue---------weibo_name :huangxiaoming-- weibo_sex : 女
setValue---------weibo_name :huangxiaoming-- weibo_sex : 男

3.2、加锁后(一个线程)

当调用setValue方法的时候,执行到sleep方法,sleep是带锁沉睡。sleep在阻塞当前线程(Thread-0)的同时,并持有该对象锁(对象Dirty_read_4 dr),所以该对象的其他同步线程(getValue())无法执行。只有等到setValue执行完,getValue才会执行。

package cn.itcast_01_mythread.thread.thread;
/*
 * 脏读
 */
public class Dirty_read_4 {
    private String weibo_name = "angel" ;
    private String weibo_sex = "女" ;
    //synchronized
    public synchronized void setValue(String weibo_name , String weibo_sex){
        System.out.println("执行setValue方法的线程是:"+Thread.currentThread().getName());
        this.weibo_name = weibo_name ;
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.weibo_sex = weibo_sex ;
        System.out.println("setValue---------weibo_name :" + weibo_name + "-- weibo_sex : "+weibo_sex);
    }
    //TODO synchronized
    public synchronized  void getValue(){
        System.out.println("执行getValue方法的线程是:"+Thread.currentThread().getName());
        System.out.println("getValue---------weibo_name :" + weibo_name + "-- weibo_sex : "+weibo_sex);
    }

    public static void main(String[] args) throws InterruptedException {
        final Dirty_read_4 dr = new Dirty_read_4();
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                dr.setValue("huangxiaoming" , "男");
            }
        }) ;
        t1.start();
        Thread.sleep(1000);
        dr.getValue();
    }
}

输出结果:

执行setValue方法的线程是:Thread-0
setValue---------weibo_name :huangxiaoming-- weibo_sex : 男
执行getValue方法的线程是:main
getValue---------weibo_name :huangxiaoming-- weibo_sex : 男
posted @ 2019-01-21 18:06  纳豆  阅读(1101)  评论(0编辑  收藏  举报