Java中synchronized关键字使用实践

版权声明:原创文章,转载请注明出处! https://blog.csdn.net/L_15156024189/article/details/84201503

1、synchronized修饰类的普通方法

package main.thread;

/**
 * Created by leboop on 2018/11/18.
 * 测试synchronized关键字使用
 */
public class SynchronizedClass {

    public void run(){
        for(int i=0;i<5;i++){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程"+Thread.currentThread().getName()+"正在打印i="+i);
        }
    }
    
    //被synchronized修饰的普通方法
    public synchronized void print() {
        System.out.println(Thread.currentThread().getName()+"线程开始执行......");
        run();
        System.out.println(Thread.currentThread().getName()+"线程执行结束......");

    }
}

测试类如下:

package main.thread;

/**
 * Created by leboop on 2018/11/18.
 */
public class SychronizedTest {
    public static void main(String[] args) {
        SynchronizedClass p=new SynchronizedClass();
        //创建t1线程
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                p.print();
            }
        });
        t1.setName("t1");

        //创建t2线程
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                p.print();
            }
        });
        t2.setName("t2");
        
        //启动线程
        t1.start();
        t2.start();

    }
}

测试类运行结果:

t1线程开始执行......
线程t1正在打印i=0
线程t1正在打印i=1
线程t1正在打印i=2
线程t1正在打印i=3
线程t1正在打印i=4
t1线程执行结束......
t2线程开始执行......
线程t2正在打印i=0
线程t2正在打印i=1
线程t2正在打印i=2
线程t2正在打印i=3
线程t2正在打印i=4
t2线程执行结束......

从运行结果来看,线程t1和线程t2是同步执行print方法的,这是因为两个线程中都是通过同一个对象p来调用print方法的,当t1开始执行p.print()时,会取得对象p的锁,此时线程t2拿不到对象p的锁无法操作p的print方法,等线程t1执行完毕释放对象p的锁,此时线程t2才可以拿到p的锁来执行print方法。如果创建两个对象p1和p2,线程t1和t2分别使用两个对象来调用print方法,此时测试类修改如下:

package main.thread;

/**
 * Created by leboop on 2018/11/18.
 */
public class SychronizedTest {
    public static void main(String[] args) {
        SynchronizedClass p1=new SynchronizedClass();
        SynchronizedClass p2=new SynchronizedClass();
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                p1.print();
            }
        });
        t1.setName("t1");


        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                p2.print();
            }
        });
        t2.setName("t2");
        t1.start();
        t2.start();

    }
}

程序运行结果如下:

t1线程开始执行......
t2线程开始执行......
线程t2正在打印i=0
线程t1正在打印i=0
线程t2正在打印i=1
线程t1正在打印i=1
线程t1正在打印i=2
线程t2正在打印i=2
线程t1正在打印i=3
线程t2正在打印i=3
线程t1正在打印i=4
t1线程执行结束......
线程t2正在打印i=4
t2线程执行结束......

此时,线程t1和t2是异步执行的,因为此时线程t1拿到的是p1对象的锁,和对象p2无关,线程t2执行p2.print时会取得p2的锁,互不影响。

2、synchronized修饰普通方法中的代码块

package main.thread;

/**
 * Created by leboop on 2018/11/18.
 * 测试synchronized关键字使用
 */
public class SynchronizedClass {

    public void run(){
        for(int i=0;i<5;i++){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程"+Thread.currentThread().getName()+"正在打印i="+i);
        }
    }

    public  void print() {
        synchronized (this){
            System.out.println(Thread.currentThread().getName()+"线程开始执行......");
            run();
            System.out.println(Thread.currentThread().getName()+"线程执行结束......");
        }
    }
}

这种方式和1相同。

3、synchronized修饰类的静态方法

package main.thread;

/**
 * Created by leboop on 2018/11/18.
 * 测试synchronized关键字使用
 */
public class SynchronizedClass {

    public static void run() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程" + Thread.currentThread().getName() + "正在打印i=" + i);
        }
    }

    public synchronized static void print() {
        System.out.println(Thread.currentThread().getName() + "线程开始执行......");
        run();
        System.out.println(Thread.currentThread().getName() + "线程执行结束......");
    }
}
package main.thread;

/**
 * Created by leboop on 2018/11/18.
 */
public class SychronizedTest {
    public static void main(String[] args) {
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                SynchronizedClass.print();
            }
        });
        t1.setName("t1");


        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                SynchronizedClass.print();
            }
        });
        t2.setName("t2");
        t1.start();
        t2.start();

    }
}

程序运行结果:

t1线程开始执行......
线程t1正在打印i=0
线程t1正在打印i=1
线程t1正在打印i=2
线程t1正在打印i=3
线程t1正在打印i=4
t1线程执行结束......
t2线程开始执行......
线程t2正在打印i=0
线程t2正在打印i=1
线程t2正在打印i=2
线程t2正在打印i=3
线程t2正在打印i=4
t2线程执行结束......

从结果来看,我们发现线程t1和线程t2是同步执行的。SynchronizedClass.print()改成对象 new SynchronizedClass().print()调用也是同步执行的。这是因为线程t1执行SynchronizedClass.print()时,取得类的锁(因为静态方法是属于类的),在未执行完前,线程t2无法取得该类的锁,当线程t1执行完后,释放类的锁,此时线程t2可以拿到类的锁去执行SynchronizedClass.print()。

4、synchronized修饰静态方法中的代码块

package main.thread;

/**
 * Created by leboop on 2018/11/18.
 * 测试synchronized关键字使用
 */
public class SynchronizedClass {

    public static void run() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程" + Thread.currentThread().getName() + "正在打印i=" + i);
        }
    }

    public  static void print() {
        synchronized(SynchronizedClass.class){
            System.out.println(Thread.currentThread().getName() + "线程开始执行......");
            run();
            System.out.println(Thread.currentThread().getName() + "线程执行结束......");
        }

    }
}

这种方式与第3种方式相同。

5、如果synchronized修饰了某个静态方法,该类是否还可以调用其他无synchronized关键字修饰的静态方法?

答案是可以的。如下:

package main.thread;

/**
 * Created by leboop on 2018/11/18.
 * 测试synchronized关键字使用
 */
public class SynchronizedClass {
    //无synchronized修饰的静态方法
    public static void otherPrint() {
        System.out.println(Thread.currentThread().getName() + "线程开始执行......");
        run();
        System.out.println(Thread.currentThread().getName() + "线程执行结束......");
    }

    //synchronized修饰的静态方法
    public synchronized static   void print() {
            System.out.println(Thread.currentThread().getName() + "线程开始执行......");
            run();
            System.out.println(Thread.currentThread().getName() + "线程执行结束......");
    }

    public static void run() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程" + Thread.currentThread().getName() + "正在打印i=" + i);
        }
    }

}

测试类如下:

package main.thread;

/**
 * Created by leboop on 2018/11/18.
 */
public class SychronizedTest {
    public static void main(String[] args) {
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                //调用synchronized修饰的静态方法
                SynchronizedClass.print();
            }
        });
        t1.setName("t1");


        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                //调用无synchronized修饰的静态方法
                SynchronizedClass.otherPrint();
            }
        });
        t2.setName("t2");
        t1.start();
        t2.start();

    }
}

程序运行结果:

t1线程开始执行......
t2线程开始执行......
线程t1正在打印i=0
线程t2正在打印i=0
线程t2正在打印i=1
线程t1正在打印i=1
线程t2正在打印i=2
线程t1正在打印i=2
线程t2正在打印i=3
线程t1正在打印i=3
线程t2正在打印i=4
线程t1正在打印i=4
t1线程执行结束......
t2线程执行结束......

从程序运行结果来看,互不影响。这是因为线程t1执行SynchronizedClass.print();时取得类锁,但是线程t2执行SynchronizedClass.otherPrint();不需要锁,可以直接执行。

6、如果synchronized修饰了某个普通方法,当某个线程通过对象调用该普通方法时,该对象是否还可以调用其他无synchronized关键字修饰的普通方法?

答案是可以的。自行测试。

7、如果类中有两个synchronized修饰的普通方法,那么两个线程是否可以通过同一个对象并发调用这两个方法?

答案是不可以的。原因是执行synchronized修饰的方法必须取得对象锁。如下:

package main.thread;

/**
 * Created by leboop on 2018/11/18.
 * 测试synchronized关键字使用
 */
public class SynchronizedClass {
    //synchronized修饰的普通方法
    public synchronized void otherPrint() {
        System.out.println(Thread.currentThread().getName() + "线程开始执行......");
        run();
        System.out.println(Thread.currentThread().getName() + "线程执行结束......");
    }

    //synchronized修饰的普通方法
    public synchronized void print() {
        System.out.println(Thread.currentThread().getName() + "线程开始执行......");
        run();
        System.out.println(Thread.currentThread().getName() + "线程执行结束......");
    }

    public void run() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程" + Thread.currentThread().getName() + "正在打印i=" + i);
        }
    }

}

测试类如下:

package main.thread;

/**
 * Created by leboop on 2018/11/18.
 */
public class SychronizedTest {
    public static void main(String[] args) {
        SynchronizedClass p=new SynchronizedClass();
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                //调用synchronized修饰的静态方法
                p.print();
            }
        });
        t1.setName("t1");


        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                //调用无synchronized修饰的静态方法
                p.otherPrint();
            }
        });
        t2.setName("t2");
        t1.start();
        t2.start();
    }
}

测试类运行结果如下:

t1线程开始执行......
线程t1正在打印i=0
线程t1正在打印i=1
线程t1正在打印i=2
线程t1正在打印i=3
线程t1正在打印i=4
t1线程执行结束......
t2线程开始执行......
线程t2正在打印i=0
线程t2正在打印i=1
线程t2正在打印i=2
线程t2正在打印i=3
线程t2正在打印i=4
t2线程执行结束......

8、如果类中有两个synchronized修饰的静态方法,那么两个线程是否可以并发调用这两个方法?

答案是不可以的。这是因为执行synchronized修饰的静态方法必须取得类锁。如下:

package main.thread;

/**
 * Created by leboop on 2018/11/18.
 * 测试synchronized关键字使用
 */
public class SynchronizedClass {
    //synchronized修饰的静态方法
    public synchronized  static void otherPrint() {
        System.out.println(Thread.currentThread().getName() + "线程开始执行......");
        run();
        System.out.println(Thread.currentThread().getName() + "线程执行结束......");
    }

    //synchronized修饰的静态方法
    public synchronized static void print() {
        System.out.println(Thread.currentThread().getName() + "线程开始执行......");
        run();
        System.out.println(Thread.currentThread().getName() + "线程执行结束......");
    }

    public static void run() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程" + Thread.currentThread().getName() + "正在打印i=" + i);
        }
    }

}

测试类如下:

package main.thread;

/**
 * Created by leboop on 2018/11/18.
 */
public class SychronizedTest {
    public static void main(String[] args) {
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                //调用synchronized修饰的静态方法
                SynchronizedClass.print();
            }
        });
        t1.setName("t1");


        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                //调用无synchronized修饰的静态方法
                SynchronizedClass.otherPrint();
            }
        });
        t2.setName("t2");
        t1.start();
        t2.start();
    }
}

测试类运行结果:

t2线程开始执行......
线程t2正在打印i=0
线程t2正在打印i=1
线程t2正在打印i=2
线程t2正在打印i=3
线程t2正在打印i=4
t2线程执行结束......
t1线程开始执行......
线程t1正在打印i=0
线程t1正在打印i=1
线程t1正在打印i=2
线程t1正在打印i=3
线程t1正在打印i=4
t1线程执行结束......

9、如果类中一个是synchronized修饰的静态方法,另一个是synchronized修饰的普通方法,那么两个线程是否可以并发调用这两个方法?

答案是可以的。这是因为一个是对象锁,一个是类锁,不是同一把锁。如下:

package main.thread;

/**
 * Created by leboop on 2018/11/18.
 * 测试synchronized关键字使用
 */
public class SynchronizedClass {
    //synchronized修饰的普通方法
    public synchronized  void otherPrint() {
        System.out.println(Thread.currentThread().getName() + "线程开始执行......");
        run();
        System.out.println(Thread.currentThread().getName() + "线程执行结束......");
    }

    //synchronized修饰的静态方法
    public synchronized static void print() {
        System.out.println(Thread.currentThread().getName() + "线程开始执行......");
        run();
        System.out.println(Thread.currentThread().getName() + "线程执行结束......");
    }

    public static void run() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程" + Thread.currentThread().getName() + "正在打印i=" + i);
        }
    }

}

测试类如下:

package main.thread;

/**
 * Created by leboop on 2018/11/18.
 */
public class SychronizedTest {
    public static void main(String[] args) {
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                //调用synchronized修饰的静态方法
                SynchronizedClass.print();
            }
        });
        t1.setName("t1");


        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                //调用无synchronized修饰的静态方法
                new SynchronizedClass().otherPrint();
            }
        });
        t2.setName("t2");
        t1.start();
        t2.start();
    }
}

运行结果如下:

t1线程开始执行......
t2线程开始执行......
线程t1正在打印i=0
线程t2正在打印i=0
线程t1正在打印i=1
线程t2正在打印i=1
线程t2正在打印i=2
线程t1正在打印i=2
线程t1正在打印i=3
线程t2正在打印i=3
线程t1正在打印i=4
线程t2正在打印i=4
t2线程执行结束......
t1线程执行结束......

注意:类锁和对象锁不是一把锁。每个对象都有一把锁与之相关。

猜你喜欢

转载自blog.csdn.net/L_15156024189/article/details/84201503