学习Java,使用synchronized关键字

学习Java,使用synchronized关键字,java,synchronized,Java,Synchronized,所以我用synchronized关键字进行测试。下面是我尝试过的一个例子: public class MyTest { static int i = 0; public static void main(String[] args) { new Thread(t1).start(); new Thread(t2).start(); } private static void countMe(String name){

所以我用
synchronized
关键字进行测试。下面是我尝试过的一个例子:

public class MyTest {
    static int i = 0;
    public static void main(String[] args) {
        new Thread(t1).start();
        new Thread(t2).start();
    }

    private static void countMe(String name){
        i++;
        System.out.println("Current Counter is: " + i + ", updated by: " + name);
    }

    private static Runnable t1 = new Runnable() {
        public void run() {
            try{
                for(int i=0; i<5; i++){
                    countMe("t1");
                }
            } catch (Exception e){}

        }
    };

    private static Runnable t2 = new Runnable() {
        public void run() {
            try{
                for(int i=0; i<5; i++){
                    countMe("t2");
                }
            } catch (Exception e){}
       }
    };
}  
当我将方法
countMe()
更改为:

private synchronized static void countMe(){
        i++;
        System.out.println("Current Counter is: " + i);
}  
我得到这个输出:

Current Counter is: 1
Current Counter is: 2
Current Counter is: 4
Current Counter is: 5
Current Counter is: 6
Current Counter is: 7
Current Counter is: 3
Current Counter is: 8
Current Counter is: 9
Current Counter is: 10  
Current Counter is: 1
Current Counter is: 2
Current Counter is: 3
Current Counter is: 4
Current Counter is: 5
Current Counter is: 6
Current Counter is: 7
Current Counter is: 8
Current Counter is: 9
Current Counter is: 10  
虽然这让我清楚地理解了
synchronized
的目的,但我想知道是否还有其他原因,我们可以使用
synchronized
。或者我在这里所做的,是我们需要使用这个
synchronized
关键字的唯一原因吗

谢谢

编辑:另一件让我感到困惑的事情是,在第一次输出中,为什么计数器在7之后变为3。这对我来说似乎有点不可能,但每次尝试都会出现类似的结果,这正常吗?

两件事:

首先,同一对象上的两个同步方法调用不可能交错。当一个线程为一个对象执行同步方法时,调用同一对象块的同步方法的所有其他线程(暂停执行),直到第一个线程对该对象执行完毕

其次,当同步方法退出时,它会自动与同一对象的同步方法的任何后续调用建立“发生在之前”关系。这保证了对象状态的更改对所有线程都可见

同步方法支持防止线程干扰和内存一致性错误的简单策略:如果一个对象对多个线程可见,则对该对象变量的所有读取或写入都通过同步方法完成。(一个重要的例外:构建对象后无法修改的final字段可以在构建对象后通过非同步方法安全地读取)


来源:

Vulkanino对你的主要问题给出了一个很好的答案,所以我只回答你关于7点后3点打印的问题

3可以在7之后打印,因为实际上语句中的字节代码比Java代码多得多

我会详细介绍一下

你打电话

System.out.println("Current Counter is: " + i);
它发生在一行Java代码中,但实际发生的是创建一个字符串,然后将该字符串传递给println。println方法本身在实际将行写入控制台之前必须进行一些处理

从概念上讲,类似以下的情况正在发生

String printlnString = "Current Counter is: 3"
--> maybe the other thread executes here
System.out.println(printlnString);
--> or maybe the other thread executes here
i is now equal to 7 and the console has "Current Counter is: 7"
println writes "Current Counter is: 3" to console

vulkanino,如果我使用线程访问同一个方法,那么每次使用synchronized是明智的做法。@911TurboS在某些方法上仅使用
synchronized
,对于正确同步多线程代码来说是极为不足的。你必须知道数据的一致性(可能还有很多其他的事情)。不能保证线程会交替。计数为3的线程可能在另一个线程计数到7之后才打印。谢谢,但是就像7之后一样,下一个调用如何打印3。我的意思是它可以打印6个甚至5个,但这似乎是新方法调用在3个之后没有读取I,这听起来很奇怪。因此,您认为每当我必须使用来自2个或更多线程的同一代码块时,始终使用synchronized应该是明智的??我建议您打印出线程的名称,这样就可以更清楚地知道混淆的位置。并发是一个复杂的话题,没有简单的答案。我的偏好是使用参与者模式,并在线程之间共享尽可能少的数据。理想情况下,没有。一个线程非常有效地执行代码通常就足够了。你不应该浪费时间去理解为什么你在7点后得到3点。你实际上很幸运,你的代码没有给你更多的垃圾;我本以为会看到一些数字两次。同步不好的代码可以做任何事情。同步是一个非常复杂的主题,它不仅仅是在两个线程调用的方法上使用
synchronized
。我强烈推荐Lea等人的“Java并发性实践”。如果你能避免任何多线程编程,那就更好了。你可能会发现这种有趣的偏见锁定是你无法“公平”访问共享资源的原因之一。其不公平的常见原因是完全公平的访问非常缓慢。通常速度很慢,最好使用一个线程。为了公平访问,您可以使用
Lock Lock=new ReentrantLock(true)
不要将计数器写入out,而是将其写入一个简单的数组,然后当线程完成时,打印出数组。