一个例子:

public class Counter {
     
    public static int count = 0;
 
    public synchronized static void inc() {
        count++;
    }
 
    public static void main(String[] args) {
 
        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Counter.inc();
                }
            }).start();
        }

    while(Thread.activeCount() > 1){
        Thread.yield();
    }

        System.out.println("运行结果:Counter.count=" + Counter.count);
    }
}

使用线程:
1.新建自定义类,继承接口Runnable,并实现方法run()。
2.实例化,作为参数传给thread(),调用thread的start方法,运行线程。该线程就会运行run()的代码。

本来,main方法执行过程便是一个线程,就好比一个人A在干活。新建一个线程,就好比又召集了一个人来同时干活。上面的例子中,这新来的人——新建的线程——将会和A一起同时地干活。
所以,如果不在main线程里判断其他100个线程是否允许完毕的话,A(main函数所在线程)可能将会比其他人有些人干的更快,那么——main函数的线程先于其他线程跑完,导致打印的结果不是最终100个线程跑完的结果。

synchronized关键字声明了该方法为同步方法。意即,同时只允许一个线程运行这个方法。前面我们说过,新建并启动了100个线程,这100个人都准备开始干活,但是带有synchronized关键字的方法只允许同时一个人使用。也就是说这100个人需要排队等待,先来后到。
为什么要同步,先来后到呢?

这里就存在内存使用的问题。
每一个线程在使用实例变量的时候,会复制一份副本到自己的栈内存中(每个线程独自享有)。倘若实例变量为5,现有两个线程同时复制了5到自己的线程内存。他们各自做加1操作,随后线程A将结果6,写回到主内存中;随后线程B也将6写回到主内存中。
但是我们的初衷可能是需要该变量作为计数器,反映所做操作的次数,但是这里两个线程共做了2次操作,变量的值却只加了1。
这个时候,使用synchronized,方法就会“同步”。一个线程做完,并将数据刷新回主内存了,下一个线程才会启动。
就好比,房子里有一个房间,想进入的人必须拿到门口的桌子上放着的一把钥匙,没有钥匙的人只可在门外等候。只有等这个人做完所有工作,并且把钥匙还回门口的桌子,下一个人才能从桌子拿到钥匙,进入房间。

volatile
Java还提供了关键字volatile。声明了volatile的变量在被赋值之后,线程会立刻将值写回主内存;在读取变量时,线程会到主内存去读取变量的最新值。说白了,它增加了线程与主内存的通信,以期望解决多线程写数据的同步问题。

但这也并不一定能保证上述计数器的问题。


杨洋的围脖啊
3 声望0 粉丝

一沙一世界,一花一天国