众所周知List不是线程安全的,我在下面所示的2个方法的代码中添加了synchronized。
1.
public class TestSyn
{
public static void main( String[] args ) throws InterruptedException
{
List<String> list = new ArrayList<>();
for( int i = 0; i < 100000; i++ )
{
new Thread(
()->
{
synchronized( list )
{
list.add( Thread.currentThread().getName() );
}
} ).start();
}
Thread.sleep( 2000 );
System.out.println( list.size() );
}
}
public class TestSyn2
{
public static void main( String[] args ) throws InterruptedException
{
List<String> list = new ArrayList<>();
synchronized( list )
{
for( int i = 0; i < 10000; i++ )
{
new Thread( () -> list.add( Thread.currentThread().getName() ) )
.start();
}
}
Thread.sleep( 2000 );
System.out.println( list.size() );
}
}
第一种方法的结果总是10000,这是正确的,而第二种方法有时会小于10000。
那么,为什么第二个不能输出正确的结果呢?
只是扩展评论中已经提供的答案(上面)。
两个版本之间的区别在于哪个或多个线程锁定
list
对象的内在互斥体。
在第一个版本中,
synchronized(list)
语句由程序创建的 100,000 个新线程中的每一个执行。它按词法包含在为每个新线程生成 Runnable
委托的 lambda 表达式中。
在第二个版本中,
synchronized(list)
语句仅由程序的main
线程执行一次。它出现在 lambda 之外。这 100000 个新线程在没有任何同步的情况下改变了共享列表。事实上 main()
在 synchronized
块内执行这一事实没有任何意义,因为没有其他线程尝试在同一个对象上同步。
仅供参考:虽然你的程序的第一个版本是“安全的”,但它也是毫无意义的。您的 100000 个新线程中没有一个在
synchronized
块之外执行任何操作。这意味着 100000 个新线程中没有一个与其他线程同时运行。但并发性是创建线程的唯一原因。
你可以这样做,从各方面来说都会更好:
public class TestSyn
{
public static void main( String[] args ) throws InterruptedException
{
List<String> list = new ArrayList<>();
for( int i = 0; i < 100000; i++ )
{
list.add( Thread.currentThread().getName() );
}
Thread.sleep( 2000 );
System.out.println( list.size() );
}
}