线程同步之synchronized关键字

今天试了试线程同步,运行代码发现和synchronized关键字介绍的不太一样。
起初代码是这样:

public class Test{
    public static void main(String args[]){
        Thread cat = new Thread(new sayhello());
        Thread dog = new Thread(new sayhello());
        dog.setName("dog");
        cat.setName("cat");
        cat.start();
        dog.start();
    }
}
class sayhello implements Runnable{
    @Override
    public synchronized void run() {
        String name = Thread.currentThread().getName();
        for(int i = 0; i < 5; i++){
            System.out.printf("%s:%d\n",name,i);
        }
    }
}

运行结果:
image
从结果上看到这里和synchronized的线程同步介绍一样,当线程cat执行run(),dog无法执行run(),等cat执行完成run()后才能让dog执行。但是可能是输出5次的时间短于jvm分配给cat执行的时间,我们把循环次数加到1000看看。
image
循环次数加到1000后明显看出线程没有同步,换句话说,cat线程执行run()方法时没有把run()方法锁起来不让别的线程使用。
另外,我还使用过Thread.sleep()方法,结果是一样的,线程不是同步的。

点击查看代码
public class Test{
    public static void main(String args[]){
        Thread cat = new Thread(new sayhello());
        Thread dog = new Thread(new sayhello());
        dog.setName("dog");
        cat.setName("cat");
        cat.start();
        dog.start();
    }
}
class sayhello implements Runnable{
    @Override
    public synchronized void run() {
        String name = Thread.currentThread().getName();
        for(int i = 0; i < 1000; i++){
            try {
                Thread.sleep(1000);
                System.out.printf("%s:%d\n",name,i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果:
image
sleep()方法会让线程休眠,说起休眠不得不提到线程的生命周期了。线程的生命周期分为新建、启动、中断、死亡,使用sleep()方法后线程处在终端状态。如果不是synchronized修饰的方法、静态代码块,线程在执行了sleep()方法后JVM会将资源分配给其他线程,但是使用synchronized修饰后一定要等待该线程执行完synchronized修饰的方法、静态代码块后才能让下一个静态代码块执行。
为什么这里的synchronized没有生效呢?原因是在这里

     Thread cat = new Thread(new sayhello());
     Thread dog = new Thread(new sayhello());

这里新建了两个sayhello类的对象,这两个对象是不共享run()这个方法的。
如果要让run()方法是线程同步的
第一种方法是给run()方法加上static关键字,这样run()方法就类方法了,两个对象共享这个类方法。但是这里有一个问题,run()是重写的方法,加了static方法后就不是重写的方法了。所以这个方案排除。
第二种方法是使用同一个sayhello对象新建两个不同线程,线程就共享run()方法了。
下面我们来看看代码:

	public class Test{
    public static void main(String args[]){
        sayhello say = new sayhello();
        Thread cat = new Thread(say);
        Thread dog = new Thread(say);
        dog.setName("dog");
        cat.setName("cat");
        cat.start();
        dog.start();
    }
}
class sayhello implements Runnable{
    @Override
    public synchronized void run() {
        String name = Thread.currentThread().getName();
        for(int i = 0; i < 1000; i++){
            System.out.printf("%s:%d\n",name,i);
        }
    }
}

运行结果:
image

既然synchronized是对线程间共享的方法、静态代码块,决定共享在于创建线程的类的对象对于这些资源是不是共享的。那么我们的代码还可以这么写:

	public class Test{
    public static void main(String args[]){
        Thread cat = new Thread(new sayhello());
        Thread dog = new Thread(new sayhello());
        dog.setName("dog");
        cat.setName("cat");
        cat.start();
        dog.start();
    }
}
class sayhello implements Runnable{
    public synchronized static void shushu(){
        String name = Thread.currentThread().getName();
        for(int i = 0; i < 1000; i++){
            System.out.printf("%s:%d\n",name,i);
        }
    }
    @Override
    public synchronized void run() {
        shushu();
    }
}

运行结果:
image
由于静态方法是所有对象共享的,所以创建的两个对象共享这个静态方法。

我们可以发现,当线程之间有共享的资源被synchronized修饰时,一个线程在使用这些资源直到执行完这些资源中的所有代码后,其他线程才能使用这些资源。
看到这里,我不禁想什么是线程?什么是线程对象?如何创建线程对象?
创建一个线程对象,可以继承Thread类然后直接新建一个对象,也可以使用Thread thread = new Thread(new XXX)来创建;或者实现Runnable方法后,使用Thread thread = new Thread(new XXX);方法创建线程。这里为什么都使用到了继承Thread的类或者实现Runnable的类创建的对象?
当一个线程启动后,会像主线程中一样顺序执行该线程中的代码。至于什么时候执行,就要看JVM如何分配。想象一下,我们开了三个线程,然后JVM就在指挥这些线程,线程1先运行1秒,然后线程2运行2秒,然后线程3……
又想到,当一个对象被创建的时候,也就重新开辟了成员方法和成员变量的入口。
除了主线程,其他线程都是执行对象类中的run()方法,run()方法也是方法,而且是一个成员方法。能调用它的,只有对应类的对象。所以这才有了需要使用对应类对象类创建线程。可以这么理解吗?线程只是拥有了在JVM哪里排队的资格,等排上队了,要最什么,就是对应类的run()方法了。这个run()方法,可以属于同一个对象的,也可以是不同对象的。同一个对象的使用synchronized就能控制run()方法在不同线程之间的同步了。在该类中的其他方法也可以使用synchronized修饰,然后在run()方法中调用即可。

稍微提取一下synchronized的关键用法:
synchronized用来控制多个线程执行同一个对象的同一个方法,只能有一个线程正在执行synchronized修饰的方法、代码块。

使用了synchronized关键字后,JVM是不是一直要把CPU的使用权交给正在执行synchronized方法、代码块的线程直到synchronized方法、代码块执行完后呢?答案是不是的。JVM一样会把CPU的使用权交给其他线程,其他线程可以执行非synchronized方法、代码块。执行到synchronized方法、代码块时线程中断。

public  class Test{
    public static void main(String args[]){
        sayhello say = new sayhello();
        Thread cat = new Thread(say);
        Thread dog = new Thread(say);
        dog.setName("dog");
        cat.setName("cat");
        cat.start();
        dog.start();
    }
}
class  sayhello implements Runnable{
    private String s = "hello";
    public synchronized  void shushu(){
        String name = Thread.currentThread().getName();
        for(int i = 0; i < 1000; i++){
            System.out.printf("%s:%d\n",name,i);
        }
    }
    @Override
    public  void run() {
        String name = Thread.currentThread().getName();
        for(int i = 0; i < 1000; i++){
            System.out.printf("%s%d\n",name,i);
        }
        shushu();
        System.out.println(name+":goodbye");
    }
}
posted on 2021-12-22 16:28  小白成长变大神  阅读(207)  评论(0编辑  收藏  举报