Java 多线程何时访问同一代码?
我的问题是:Java 多线程何时访问同一代码?,java,multithreading,thread-safety,Java,Multithreading,Thread Safety,我的问题是: 默认情况下,Java程序是否只导致创建一个线程 如果是,并且如果我们创建一个多线程程序,那么多个线程何时访问Java对象的相同代码 例如,我有一个Java程序,它有两个方法-add()和sub()。在什么情况下,2个或更多线程将运行“add()”方法 代码不总是线程安全的吗,因为多个线程将访问代码的不同部分 如果不是,请给出一个线程安全是一个问题的示例程序 取决于实施情况。只有一个线程(“主线程”)将调用publicstaticvoidmain(String[])方法,但这并不意味
publicstaticvoidmain(String[])
方法,但这并不意味着其他线程没有为其他任务启动import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) throws InterruptedException {
List<Object> list = new ArrayList<>();
Runnable action = () -> {
while (true) {
list.add(new Object());
}
};
Thread thread1 = new Thread(action, "tread-1");
thread1.setDaemon(true); // don't keep JVM alive
Thread thread2 = new Thread(action, "thread-2");
thread2.setDaemon(true); // don't keep JVM alive
thread1.start();
thread2.start();
Thread.sleep(1_000L);
}
}
import java.util.ArrayList;
导入java.util.List;
公共班机{
公共静态void main(字符串[]args)引发InterruptedException{
列表=新的ArrayList();
可运行操作=()->{
while(true){
添加(新对象());
}
};
螺纹1=新螺纹(动作“踏板-1”);
thread1.setDaemon(true);//不要让JVM保持活动状态
螺纹2=新螺纹(动作“螺纹2”);
thread2.setDaemon(true);//不要让JVM保持活动状态
thread1.start();
thread2.start();
线程睡眠(1_000L);
}
}
ArrayList
不是线程安全的。上面的代码有两个线程不断尝试将新的对象
添加到同一数组列表
大约一秒钟。这并不能保证,但如果您运行该代码,您可能会看到ArrayIndexOutOfBoundsException
或类似的内容。无论抛出任何异常,ArrayList
的状态都有被破坏的危险。这是因为状态由多个线程更新,没有同步System.out.println()
调用可能只创建一个线程。但是,例如,一旦您打开Swing GUI窗口,至少会有一个其他线程(对用户输入作出反应并负责UI更新的“事件调度程序线程”)
2) 如果是,并且如果我们创建一个多线程程序,那么多个线程何时访问Java对象的相同代码
你这方面的误解。对象没有代码。基本上,线程将运行特定的方法;它自己的run()。然后线程只执行该方法,以及从该初始方法触发的任何其他方法调用
当然,在运行该代码时,该线程可能会创建其他对象,或者操纵现有对象的状态。当每个线程只接触一组不同的对象时,就不会出现问题。但是一旦有多个线程处理相同的对象状态,就需要采取适当的预防措施(以避免不确定的行为)。不要考虑“代码部分”,要考虑数据所在的位置以及有多少线程正在访问实际数据
- 局部变量位于使用它们的线程堆栈上,并且是线程安全的,因为它们是每个线程的不同数据“容器”
- 堆上的任何数据,如实例或静态字段,本质上都不是线程安全的,因为如果有多个线程访问该数据,那么它们可能存在争用
我们可以更复杂地讨论数据的真实位置,但这个基本的解释应该会让你对发生的事情有一个很好的了解
下面的代码给出了一个由两个线程共享的实例示例,在本例中,两个线程都在访问相同的数组列表,该列表指向堆中的相同数组数据容器。运行几次,您最终会看到失败。如果你注释掉其中一个线程,它每次都会正常工作,从99开始倒数
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
MyRunnable r = new MyRunnable();
new Thread(r).start();
new Thread(r).start();
}
public static class MyRunnable implements Runnable {
// imagine this list living out in the heap and both threads messing with it
// this is really just a reference, but the actual data is in the heap
private List<Integer> list = new ArrayList<>();
{ for (int i = 0; i < 100; i++) list.add(i); }
@Override public void run() {
while (list.size() > 0) System.out.println(list.remove(list.size() - 1));
}
}
}
import java.util.ArrayList;
导入java.util.List;
公共班机{
公共静态void main(字符串[]args){
MyRunnable r=新的MyRunnable();
新线程(r.start();
新线程(r.start();
}
公共静态类MyRunnable实现Runnable{
//想象一下这个列表存在于堆中,两个线程都在处理它
//这实际上只是一个引用,但实际数据在堆中
私有列表=新的ArrayList();
{for(inti=0;i<100;i++)list.add(i);}
@重写公共无效运行(){
而(list.size()>0)System.out.println(list.remove(list.size()-1));
}
}
}
您的问题表明您可能不完全理解“线程”的含义
当我们学习编程时,他们告诉我们计算机程序是一系列指令,他们告诉我们计算机从某个定义良好的入口点(例如,main()
例程)开始逐个执行这些指令
好的,但是当我们谈论多线程程序时,仅仅说“计算机”执行我们的代码是不够的。现在我们说线程执行我们的代码。每个线程对它在程序中的位置都有自己的想法,如果两个或多个线程碰巧同时在同一个函数中执行,那么每个线程都有自己的函数参数和局部变量的私有副本
所以,你问:
是一个Java程序吗
class MyRunnable implements Runnable {
public void run() {
DoSomeUsefulThing();
DoSomeOtherThing();
}
}
MyRunnable r = new MyRunnable();
Thread t = new Thread(r);
t.start();
...
...
Thread t = new Thread(r);
t.start();
DoSomeUsefulThing();
...
class MyDataClass { ... }
Class MyRunnable implements Runnable {
private MyDataClass data;
public MyRunnable(MyDataClass data) {
this.data = data;
}
public void run() {
DoSomeUsefulThingWITH(data);
DoSomeOtherThingWITH(data);
}
}
MyDataClass dat_a = new MyDataClass(...);
MyDataClass dat_b = new MyDataClass(...);
MyRunnable r = new MyRunnable(dat_a);
Thread t = new Thread(r);
t.start();
DoSomeUsefulThingWITH(dat_b);