作者:半身风雪 上一篇:线程之间的共享和协作
一个Java 程序从main() 方法开始执行,然后按照既定的代码逻辑执行,看似没有其他线程参与,但实际上Java 程序天生就是多线程程序,因为执行main() 方法的是一个名称为main 的线程。
public static void main(String[] args) {
// java 虚拟机线程系统的管理接口
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
// 不需要获取同步的monitor 和synchronizer 信息,仅仅获取线程和线程堆栈信息
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
// 遍历线程,仅打印线程ID 和线程名称信息
for (ThreadInfo threadInfo : threadInfos) {
System.out.println("线程ID" + threadInfo.getThreadId() + "线程名" + threadInfo.getThreadName());
}
}上面代码输出的结果:

从上面的例子中,我们能发现,在Java中短短的几行代码,就给我们启动了5个线程,当然,不同的版本,启动的线程数量也不一样,由此我们可以得出:**Java 天生就是多线程的**
线程的启动方式有两种(源码中的注释是这么写的)参见代码:cn.enjoyedu.ch1.base.NewThread:
示例代码:(派生自Thread 类,来实现我们的两种线程启动方式)
/**
* 扩展自Thread 类
*/
private static class UserThread extends Thread{
public void run() {
System.out.println("UserThread.run");
}
}
/**
* 扩展自 Runnable 类
*/
private static class UserRunnable implements Runnable {
public void run() {
System.out.println("UserRunnable.run");
}
}
public static void main(String[] args) {
UserThread userThread = new UserThread();
userThread.start();
UserRunnable userRunnable = new UserRunnable();
new Thread(userRunnable).start();
}Thread 是Java 里对线程的唯一抽象。Runnable 是Java对任务(业务逻辑)的抽象。Thread 可以接受任意一个Runnable 的实例并执行。线程自然终止:要么是run 执行完成了,要么是抛出了一个未处理的异常导致线程提前结束。stop:暂停、恢复和停止操作对应在线程Thread的API就是suspend()、resume() 和 stop()。但是这些API都是过期的,不再建议使用。不建议使用的主要原因有:以suspend()方法为例,在调用后,线程不会释放已占有的资源(比如锁),而是占有资源进入睡眠状态,这样容易引发死锁问题。同样,stop() 方法在终结一个线程时,不会保证线程的资源正常释放,通常是没有给予线程完成资源释放的机会,因此会到导致程序可能工作在不确定的状态下。整因为suspend()、resume() 和 stop() 方法带来的副作用,这些方法才会被标注为不建议使用的过期方法中。中断:安全的中止则是其它线程通过调用线程A的interrupt() 方法对其进行中止操作,中断代表着其它线程对A线程打了个招呼,“A, 你要中断了”,不代表线程A 会立即停止自己的工作,同样A线程可以不理会这种请求。因为Java 中的线程是协作式的,不是抢占式。线程通过检查自身的中断标志位是否被置为true来进行响应。private static class UserThread extends Thread{
public UserThread(String name){
super(name);
}
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + "interrupt flag = " + isInterrupted());
while (!isInterrupted()){
// while (!Thread.interrupted()){
// while (true){
System.out.println(threadName+ "is running");
System.out.println(threadName+ "inner interrupt flag = "+ isInterrupted());
}
System.out.println(threadName+ "interrupt flag = " + isInterrupted());
}
}
public static void main(String[] args) {
Thread endTread = new UserThread("endTread");
endTread.start();
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 中断线程, 其实设置线程的标识位
endTread.interrupt();
}运行上面的代码:

isInterrupted() 进行线程中断的之后,isInterrupted()会返回一个true。我们一起来看一下isInterrupted() 方法的源码:
静态的 interrupted() 方法,他返回的也是一个bool 值,先看一下这个方法的源码:
由此我们可以总结出:**线程通过方法 isInterrupted()来进行判断是否被中断,也可以调用静态方法 Thread.interrupted()来进行判断当前线程是否被中断,不过 Thread.interrupted() 会同时将中断标识位改写为 false。**如线程调用了 thread.sleep、thread.join、 thread.wait 等),则线程在检查中断标识时,如果发现中断标识位true,则会在这些阻塞方法调用处抛出InterruptedException 异常,并且在抛出异常后会立即将线程的中断标识位清除,即重新设置为true。private static class UserThread extends Thread{
public UserThread(String name){
super(name);
}
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + "interrupt flag = " + isInterrupted());
while (!isInterrupted()){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
System.out.println(threadName+ "inner interrupt flag = "+ isInterrupted());
e.printStackTrace();
}
System.out.println(threadName+ "is running");
}
System.out.println(threadName+ "interrupt flag = " + isInterrupted());
}
}
public static void main(String[] args) {
Thread endTread = new UserThread("endTread");
endTread.start();
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 中断线程, 其实设置线程的标识位
endTread.interrupt();
}上面代码运行结果:

catch 中调用interrupt() 方法就可以了
代码运行结果:


new Thread() 其实只是new出一个thread的实例,还没有和操作系统中真正的线程挂起勾来。只有执行了start() 方法后,才实现了真正意义上的启动线程。start() 方法让一个线程进入就绪队列等待分配CPU,分到CPU后才调用run()方法,start() 方法不能重复调用,如果重复调用,就会抛出异常。那么start() 和 run() 有什么区别呢?请看代码:
private static class UserThread extends Thread{
public void run() {
int i = 90;
while (i > 0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("I am "+Thread.currentThread().getName()+"and now the i="+ i--);
}
}
}
public static void main(String[] args) {
Thread endTread = new UserThread();
endTread.setName("threadRun");
endTread.start();
}代码运行结果:(观察运行结果,我们可以得出,调用start() 方法的时候,执行start() 方法的是子线程)

我们修改一下代码,调用run() 方法
public static void main(String[] args) {
Thread endTread = new UserThread();
endTread.setName("threadRun");
endTread.run();
}查看运行结果:(观察运行结果,我们可以得出,调用run() 方法的时候,执行run() 方法的是主线程)

static class Students implements Runnable {
private Thread thread;
public Students(Thread thread) {
this.thread = thread;
}
public Students() {
}
public void run() {
System.out.println("学生开始排队打饭。。。。。");
try {
if (thread != null) thread.join();
// 休眠2 秒
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("学生打饭完成");
}
}
static class Teacher implements Runnable {
public void run() {
try {
// 休眠2 秒
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("老师开始打饭、、、、、");
System.out.println(Thread.currentThread().getName() + "老师打饭完成。");
}
}
public static void main(String[] args) throws InterruptedException {
Teacher teacher = new Teacher();
Thread teaThread = new Thread(teacher);
Students students = new Students(teaThread);
Thread stuThread = new Thread(students);
stuThread.start();
teaThread.start();
System.out.println("我开始打饭、、、、、");
stuThread.join();
// 让主线程休眠2 秒
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName()+"我打饭完成");
}代码运行结果如下:
由上代码运行结果,我们可以得出:**在线程B中调用了线程A的join() 方法,只到线程A 执行完毕后,才会继续执行线程B的。**setPriority(int)方法来修改优先级,默认 优先级是 5,优先级高的线程分配时间片的数量要多于优先级低的线程。Thread.setDaemon(true)将线程设置 为Daemon线程。我们一般用不上,比如垃圾回收线程就是Daemon线程。finally 块并不一定会执行。在构建Daemon线程时,不能依靠finally 块中 的内容来确保执行关闭或清理资源的逻辑。下面我们看一段代码,在main 方法中启动两个线程,每个线程的count 值都是10000,两个线程的和应该就是20000。
public class OnlyMain {
private long count = 0;
private Object object = new Object();
public long getCount() {
return count;
}
public void incCount() {
count++;
}
// 线程
private static class Count extends Thread {
private OnlyMain onlyMain;
public Count(OnlyMain onlyMain) {
this.onlyMain = onlyMain;
}
public void run() {
for (int i = 0; i < 10000; i++) {
// count = count++ = 10000
onlyMain.incCount();
}
}
}
public static void main(String[] args) throws InterruptedException {
OnlyMain onlyMain = new OnlyMain();
// 启动两个线程
Count count1 = new Count(onlyMain);
Count count2 = new Count(onlyMain);
count1.start();
count2.start();
Thread.sleep(50);
System.out.println(onlyMain.count);
}
}代码的运行结果是:
经过多次运行,每次运行的结果都不一样,只有在很少很少的几率的情况下,才会出现正确的20000结果值,这是为什么呢?修改上面代码中的incCount() 方法,添加一个内锁:
public synchronized void incCount() {
count++;
}这样我们就能保证每次运行的正确结果了:



?$\textcolor{blue}{原创不易,还希望各位大佬支持一下}$ <br/> ? $\textcolor{green}{点赞,你的认可是我创作的动力!}$ <br/> ? $\textcolor{green}{收藏,你的青睐是我努力的方向!}$ <br/> ✏️ $\textcolor{green}{评论,你的意见是我进步的财富!}$ <br/>
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
这里是Ruby新手。完成一些练习后碰壁了。练习:计算一系列成绩的字母等级创建一个方法get_grade来接受测试分数数组。数组中的每个分数应介于0和100之间,其中100是最大分数。计算平均分并将字母等级作为字符串返回,即“A”、“B”、“C”、“D”、“E”或“F”。我一直返回错误:avg.rb:1:syntaxerror,unexpectedtLBRACK,expecting')'defget_grade([100,90,80])^avg.rb:1:syntaxerror,unexpected')',expecting$end这是我目前所拥有的。我想坚持使用下面的方法或.join,
我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/
我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("
我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www
我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我
什么是ruby的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht
在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList()Obt
这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/
HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候