当线程处于活动状态时不处于(可运行)状态
When a thread is not in (Runnable) state while active
希望你们一切都好。所以来问这个问题。我有这部分代码:
private static ArrayList<String> primelist = new ArrayList<>();
static void addToList(String list_elm) {
primelist.add(list_elm);
}
基本上这个列表被我在完整代码(下面)中创建的多个线程同时访问,这些线程负责进行一些计算和 return 结果将是通过调用方法 addToList(String list_elm)
添加到此 primelist
。
但是,在所有线程都终止(即:完成它们的工作)之后,primelist
中存在一些空值。因此,经过一些研究,结果表明 ArrayList 不是线程安全的 class,因此它的方法也不是。我想问以下(也许很深)的问题:
线程在执行一行代码时是否会进入(waiting//timed waiting)..,也就是说它正在调用方法addToList(String list_elm)
并且它到达了[=行16=] 但在添加元素时它恰好停止了!
如果不能,请您澄清我对(尤其是)ArrayList 案例的困惑。 (((基本上是怎么回事?^^))
完整代码:
import java.util.ArrayList;
import java.util.Iterator;
public class CreatingAThreadThree
{
private static ArrayList<String> primelist = new ArrayList<>();
static void addToList(String list_elm)
{
primelist.add(list_elm);
}
static ArrayList<String> getListReference(){
return primelist;
}
public static void main(String[] args)
{
for(long x = 6223372036854775899L; x<=(6223372036854775999L); x+=2)
{
new Thread (new MyCalcRunnable(x)).start();
}
for(long x = 9223372036854774703L; x<=9223372036854774789L; x+=2 )
{
new MyCalcThread(x, "myChildThread"+x);
}
Thread mainThread = Thread.currentThread();
int spinner =0;
char animation = ' ';
System.out.println("Total number of active threads: " + Thread.activeCount());
System.out.print("Calculating primes: ");
while(Thread.activeCount() >1)
{
spinner ++;
switch(spinner)
{
case 1:
animation = '|';
break;
case 2:
animation = '/';
break;
case 3:
animation = '-';
break;
case 4:
animation = '\';
spinner = 0;
break;
}
System.out.print("\b" + animation);
try
{
Thread.sleep(200);
}catch(InterruptedException ex)
{
}
}
System.out.println("Total number of active threads: " + Thread.activeCount());
System.out.println("Results List:");
Iterator<?> iterator = (getListReference().iterator());
while(iterator.hasNext())
{
System.out.println(iterator.next());
}
}
}
class MyCalcThread extends Thread
{
private long numberToFactor = 0;
MyCalcThread(long numberToFactor, String name)
{
super(name);
this.numberToFactor = numberToFactor;
start();
}
@Override
public void run()
{
CreatingAThreadThree.addToList(new PrimeStuff().isItPrime(this.numberToFactor));
}
}
class MyCalcRunnable implements Runnable
{
private long numberToFactor = 0;
MyCalcRunnable(long numberToFactor)
{
this.numberToFactor = numberToFactor;
}
@Override
public void run()
{
CreatingAThreadThree.addToList(new PrimeStuff().isItPrime(this.numberToFactor));
}
}
class PrimeStuff
{
String isItPrime(long numberToFactor)
{
if(numberToFactor % 2 == 0)
return (numberToFactor +"is Not prime....divisible by 2");
long squareRoot = (long)(Math.sqrt(numberToFactor));
for(long i=3; i<squareRoot; i++)
{
if(numberToFactor % i == 0)
{
return (numberToFactor +"is Not prime....first divisible by " + i);
}
}
return (numberToFactor + " is Prime!!");
}
}
你关注的问题不对;意思是:花时间修复损坏的代码。
当您有多个线程访问同一个共享的、未受保护的数据时;各种各样的事情都可能发生。
另外:仅更改您正在使用的列表的类型可能是不够的。您会看到,CopyOnWrite 列表保证 "thread safety" 用于单个操作。但是当你有像
这样的东西时
if (someList.size() > 1) {
do something with your list
仍然不安全;即使在使用 CopyOnWrite 列表时——因为列表上有两个调用;并且列表可能会在第一次和第二次通话之间发生变化;当其他线程同时更改列表时。
长话短说:一种解决方案是对您的方法使用synchronized
,而您不想要运行 并行.
换句话说:添加您需要的保护;而不是通过了解线程状态模型的细节来混淆自己 - 那部分不会帮助你编写正确的代码。
鉴于您的评论:您尝试在非常 "low" 的水平上解决这个问题。您认为您必须了解线程状态和等待条件等才能得出 "good" 解决方案。但这不是一种有效的方法;特别是当您是新手并学习这些东西时。
首先你应该担心正确的解决方案。然后你可以继续前进并增强它;例如通过做不同的实验。从这个意义上说,您应该明白:在真正的多线程中,我们通常会尝试从如此底层的细节中抽象。相反,我们甚至引入 额外的 层,例如 Executors。
我想告诉你的是:调查低层次的细节很可能不会帮助你,但在这一点上会让你负担过重。
竞争条件可能发生在任何地方,不仅在您编写的代码中,而且在 ArrayList 的实现中。
即使以最简单的形式添加到 ArrayList 也将涉及以下操作:
- 读取
size
变量
- 将数据写入
elementData[size]
- 增加
size
变量
当添加操作增加尺寸大于elementData
的长度时,也会发生调整尺寸:
- 创建一个比当前数组大 1.5 倍的新数组
elementData
- 将
elementData
中的值复制到新数组中
- 将新数组分配给
elementData
- 将数据写入
elementData[size]
- 增加
size
变量
在您的情况下,如果将 null 添加到列表中,则可能是两个线程同时尝试调整数组大小。假设目前您的列表有 10 个元素。 elementData
的长度为 10。因此添加第 11 个值将调整 elementData
的大小
- 线程 1 创建一个长度为 15 的新数组 X
- 线程 1 将数据从
elementData
复制到新数组
- 线程 2 创建一个长度为 15 的新数组 Y
- 线程 2 将数据从
elementData
复制到新数组
- 线程 1 将 X 分配给
elementData
- 线程 1 将值写入
elementData[10]
- 线程 1 递增
size
到 11
- 线程 2 将 Y 分配给
elementData
- 线程 2 将值写入
elementData[11]
- 线程 2 递增
size
到 12
- elementData[10] 结果将为空
第 8 步发生的事情是线程 2 覆盖了线程 1 更改的 elementData
,因此 elementData[10](由线程 1 写入)丢失了。
希望你们一切都好。所以来问这个问题。我有这部分代码:
private static ArrayList<String> primelist = new ArrayList<>();
static void addToList(String list_elm) {
primelist.add(list_elm);
}
基本上这个列表被我在完整代码(下面)中创建的多个线程同时访问,这些线程负责进行一些计算和 return 结果将是通过调用方法 addToList(String list_elm)
添加到此 primelist
。
但是,在所有线程都终止(即:完成它们的工作)之后,primelist
中存在一些空值。因此,经过一些研究,结果表明 ArrayList 不是线程安全的 class,因此它的方法也不是。我想问以下(也许很深)的问题:
线程在执行一行代码时是否会进入(waiting//timed waiting)..,也就是说它正在调用方法addToList(String list_elm)
并且它到达了[=行16=] 但在添加元素时它恰好停止了!
如果不能,请您澄清我对(尤其是)ArrayList 案例的困惑。 (((基本上是怎么回事?^^))
完整代码:
import java.util.ArrayList;
import java.util.Iterator;
public class CreatingAThreadThree
{
private static ArrayList<String> primelist = new ArrayList<>();
static void addToList(String list_elm)
{
primelist.add(list_elm);
}
static ArrayList<String> getListReference(){
return primelist;
}
public static void main(String[] args)
{
for(long x = 6223372036854775899L; x<=(6223372036854775999L); x+=2)
{
new Thread (new MyCalcRunnable(x)).start();
}
for(long x = 9223372036854774703L; x<=9223372036854774789L; x+=2 )
{
new MyCalcThread(x, "myChildThread"+x);
}
Thread mainThread = Thread.currentThread();
int spinner =0;
char animation = ' ';
System.out.println("Total number of active threads: " + Thread.activeCount());
System.out.print("Calculating primes: ");
while(Thread.activeCount() >1)
{
spinner ++;
switch(spinner)
{
case 1:
animation = '|';
break;
case 2:
animation = '/';
break;
case 3:
animation = '-';
break;
case 4:
animation = '\';
spinner = 0;
break;
}
System.out.print("\b" + animation);
try
{
Thread.sleep(200);
}catch(InterruptedException ex)
{
}
}
System.out.println("Total number of active threads: " + Thread.activeCount());
System.out.println("Results List:");
Iterator<?> iterator = (getListReference().iterator());
while(iterator.hasNext())
{
System.out.println(iterator.next());
}
}
}
class MyCalcThread extends Thread
{
private long numberToFactor = 0;
MyCalcThread(long numberToFactor, String name)
{
super(name);
this.numberToFactor = numberToFactor;
start();
}
@Override
public void run()
{
CreatingAThreadThree.addToList(new PrimeStuff().isItPrime(this.numberToFactor));
}
}
class MyCalcRunnable implements Runnable
{
private long numberToFactor = 0;
MyCalcRunnable(long numberToFactor)
{
this.numberToFactor = numberToFactor;
}
@Override
public void run()
{
CreatingAThreadThree.addToList(new PrimeStuff().isItPrime(this.numberToFactor));
}
}
class PrimeStuff
{
String isItPrime(long numberToFactor)
{
if(numberToFactor % 2 == 0)
return (numberToFactor +"is Not prime....divisible by 2");
long squareRoot = (long)(Math.sqrt(numberToFactor));
for(long i=3; i<squareRoot; i++)
{
if(numberToFactor % i == 0)
{
return (numberToFactor +"is Not prime....first divisible by " + i);
}
}
return (numberToFactor + " is Prime!!");
}
}
你关注的问题不对;意思是:花时间修复损坏的代码。
当您有多个线程访问同一个共享的、未受保护的数据时;各种各样的事情都可能发生。
另外:仅更改您正在使用的列表的类型可能是不够的。您会看到,CopyOnWrite 列表保证 "thread safety" 用于单个操作。但是当你有像
这样的东西时if (someList.size() > 1) {
do something with your list
仍然不安全;即使在使用 CopyOnWrite 列表时——因为列表上有两个调用;并且列表可能会在第一次和第二次通话之间发生变化;当其他线程同时更改列表时。
长话短说:一种解决方案是对您的方法使用synchronized
,而您不想要运行 并行.
换句话说:添加您需要的保护;而不是通过了解线程状态模型的细节来混淆自己 - 那部分不会帮助你编写正确的代码。
鉴于您的评论:您尝试在非常 "low" 的水平上解决这个问题。您认为您必须了解线程状态和等待条件等才能得出 "good" 解决方案。但这不是一种有效的方法;特别是当您是新手并学习这些东西时。
首先你应该担心正确的解决方案。然后你可以继续前进并增强它;例如通过做不同的实验。从这个意义上说,您应该明白:在真正的多线程中,我们通常会尝试从如此底层的细节中抽象。相反,我们甚至引入 额外的 层,例如 Executors。
我想告诉你的是:调查低层次的细节很可能不会帮助你,但在这一点上会让你负担过重。
竞争条件可能发生在任何地方,不仅在您编写的代码中,而且在 ArrayList 的实现中。
即使以最简单的形式添加到 ArrayList 也将涉及以下操作:
- 读取
size
变量 - 将数据写入
elementData[size]
- 增加
size
变量
当添加操作增加尺寸大于elementData
的长度时,也会发生调整尺寸:
- 创建一个比当前数组大 1.5 倍的新数组
elementData
- 将
elementData
中的值复制到新数组中 - 将新数组分配给
elementData
- 将数据写入
elementData[size]
- 增加
size
变量
在您的情况下,如果将 null 添加到列表中,则可能是两个线程同时尝试调整数组大小。假设目前您的列表有 10 个元素。 elementData
的长度为 10。因此添加第 11 个值将调整 elementData
- 线程 1 创建一个长度为 15 的新数组 X
- 线程 1 将数据从
elementData
复制到新数组 - 线程 2 创建一个长度为 15 的新数组 Y
- 线程 2 将数据从
elementData
复制到新数组 - 线程 1 将 X 分配给
elementData
- 线程 1 将值写入
elementData[10]
- 线程 1 递增
size
到 11 - 线程 2 将 Y 分配给
elementData
- 线程 2 将值写入
elementData[11]
- 线程 2 递增
size
到 12 - elementData[10] 结果将为空
第 8 步发生的事情是线程 2 覆盖了线程 1 更改的 elementData
,因此 elementData[10](由线程 1 写入)丢失了。