class ThreadDemo extends Thread
{
public void run()
{
for(int i =0; i<5;i++)
{
System.out.println(i);
}
}
}
class ThreadApp
{
public static void main(String args[])
{
ThreadDemo thread1 = new ThreadDemo();
thread1.start();
ThreadDemo thread2 = new ThreadDemo();
thread2.start();
ThreadDemo thread3 = new ThreadDemo();
thread3.start();
}
}
输出:
0
2
3
1
4
1
2
4
3
0
0
1
2
3
4
默认情况下,java应用程序是单线程应用程序。我们将使用称为多线程的概念来共享工作。意思是说,如果我们创建线程,那么它将简化工作,而不是使用一个线程(主线程)来完成工作。我从理论上理解这件事。当我开始编写代码时,我的疑问就出现了。在上面的程序中,我创建了3个线程。如果3个线程在同一逻辑上工作(迭代并使用for循环打印值),为什么它要给出3个单独的输出,而不是给出一组0到4的值?
2条答案
按热度按时间sxpgvts31#
试着做一些不同的测试,看看自己是从哪里来的
可能输出:
关于福利,只有一个提示
Producer-Consumer
问题。。。m4pnthwp2#
复制工作,而不是分享工作
你说:
我们将使用称为多线程的概念来共享工作。
但你没有分享这份工作。你重复了这项工作。而不是运行
for
循环一次,你跑了for
循环三次,每三个线程一次。你问:
为什么它给出3个独立的输出,而不是给出一组0到4的值?
如果一个学校老师让三个学生在黑板上写字母表,我们最终得到的不是26个字母,而是78个字母(3*26)。每个学生都会在字母表的字母间循环。同样地,三个线程中的每一个都通过0到4的计数循环。
你的
for
循环在任务代码中是本地的。所以每个线程都从顶部开始运行所有代码。所以for
循环执行三次,每个线程一次。注意:system.out打印不正常
发送文本到
System.out
通过呼叫println
或者print
不会导致文本立即按发送顺序出现。您发送的文本行可能会出现错误。在检查语句序列时,始终包含一个时间戳,例如
java.time.Instant.now()
. 然后研究输出。您可能需要使用文本编辑器手动重新排序输出以查看真实序列。您可以在下面我自己的示例输出中看到不按时间顺序排列的行。
执行人服务
在现代java中,我们不再需要解决
Thread
直接上课。通常最好使用executors框架。请参见oracle教程。当你跑的时候。
织布机项目
projectloom有望为java带来一些新特性,比如虚拟线程(纤维)和
ExecutorService
是AutoCloseable
与资源一起使用时,请尝试自动关闭。让我们重写上面的代码以使用projectloom技术。基于早期访问java16的初步构建现在可用。
此外,我们可以用更简单的lambda语法重写上面看到的匿名类。
与上面的另一个区别是:虚拟线程没有名称。因此,我们切换到使用线程的id号来区分正在运行的线程。
当你跑的时候。
跨线程共享状态
如果您真的想在线程之间共享值,可以在直接任务代码之外定义它们。
在下一个示例中,我们定义一个类
Counter
实现Runnable
. 作为一个Runnable
我们可以将此类的示例传递给执行器服务。我们定义了一个成员字段ConcurrentMap
(线程安全)Map
)跟踪我们想要的数字0-4。对于这五个数字中的每一个,我们Map到虚拟线程的id号,该虚拟线程能够击败其他虚拟线程,从而将该条目提交到最初的空Map中。请注意,我们正在提交一份
Counter
对象到所有三个线程。所以这三个线程都可以访问相同的ConcurrentMap
对象。这就是为什么我们必须使用ConcurrentMap
而不是平原Map
. 任何跨线程共享的资源都必须构建为线程安全的。我们正在打电话
Thread.sleep
把事情搞混。否则,第一个线程可能会在主线程仍提交给第二个和第三个线程时完成所有工作。这是一个
main
让我们练习的方法。在这个特定运行的结果中,我们可以看到编号为16和17的两个线程成功地将条目放入了Map中。第三个线程不能第一个放入五个条目中的任何一个。
counter.results={0=16,1=17,2=17,3=16,4=16}