java多线程and线程通信

 一:线程与进程

进程是程序的一次动态的执行过程。多进程操作系统能同时运行多个进程(程序),由于cpu具备分时机制,所以每个进程都能循环获得自己的cpu时间片段。由于cpu执行速度非常快,是的所有程序好像在同时运行一样。

  线程是比进程更小的执行单位,线程是在进程的基础上经一部划分的。这些线程可以同时存在、同时运行。一个进程可能包括多个同时执行的线程。所谓线程(Thread)是指程序的运行流程,多线程机制则是指可以同时运行多个程序块,使程序运行的效率变得更高。

二:java实现多线程操作有两种方式

继承Thread类,实现Runnable接口。多线程之间是交错运行的。



编译并运行程序后,发现以上程序时先执行完m1对象在执行m2对象,并没有交错运行,也就是说线程实际上并没有被启动,还是属于顺序执行的方式。那么如何启动线程呢?

调用从Thread类继承而来的start()方法。将run()修改为start()就行

解释:为什启动线程不能直接调用run()方法呢?

因为线程的运行需要本机的操作系统的支持,run()方法属于java虚拟机栈(执行java服务,也就是字节码服务),而start方法(实际上调用的是star0方法)属于本地方法栈(执行native方法服务),native服务适用于本地操作系统。

使用Runnable接口启动多线程



三:线程的状态

1创建状态:
调用Thread thread = new Thread();线程对象便处于新建状态,此时,它已经有了相应的内存空间和其他资源
2就绪状态:
调用start()方法后,线程进入就绪状态,等待cpu服务。
3运行状态:
当调用run()方法时,程序进入运行状态,获得了处理器资源

4堵塞状态:
如果调用sleep、suspend、wait等方法,线程进入堵塞状态,只有当堵塞的原因消除之后,线程才可以转入就绪状态

5死亡状态:

线程调用stop()方法或run()方法执行结束后,就处于死亡状态。处于死亡状态的线程不具有运行能力。

四:线程操作的相关方法

0最近在看java多线程,偶然看到一个java线程通信的知识,这里作为补充吧

一、采用synchronized保证线程同步时如何实现线程通信
     java Object类实现了wait(),notify(),notifyAll()三种方法,这三种方法是实现线程通信的关键所在。并且这三种方法必须由同步监视器(所谓同步监视器就是个锁,也就是synchronized(锁))调用,如果synchronized锁的对象和实现了wait(),notify(),notifyAll()这三种方法的对象不是一个的话,将会抛出 java.lang.IllegalMonitorStateException异常。接下来我们了解下这三个方法是作用。
  • wait():导致当前线程等待,直到其他线程调用该同步监视器(即锁)的notify()或notifyAll()方法时来唤醒该线程。同时调用wait()方法的当前线程会释放对该同步监视器的锁定(翻译成人话就是:当调用wait方法时,会释放这个同步锁,也就是synchronized锁定的那个对象,也就是调用wait()方法的对象)。
  • notify():调用notify()方法时,会唤醒由于阻塞在同一个同步监视器调用了wait()方法的线程,翻译成书面语就是,唤醒在此同步监视器上等待的当个线程,如果在此同步监视器上阻塞着很多线程,则会选择一个唤醒,至于唤醒那个线程,这基于JVM对线程的调度。
  • notifyAll():唤醒在此同步监视器上阻塞的所有线程。也就是唤醒所有线程由于调用了该同步监视器的await方法而导致阻塞。
编程注意事项:
wait(),notify(),notifyAll()这三种方法的调用必须在synchronized方法或者块中


虽然多线程是交替运行的,但是要想输出结果严格按照上面来,还是必须要一定手段的。

在这里,首先在同步代码块中加入了一个notify方法,用于唤醒下一个线程。然后,在代码块的执行结束地方加入一个wait方法,用于使当前线程等待。或许有人想问,为什么要用notify方法?如果所有线程都wait了,那么谁来执行呢?所以要用notify。

这里补充一下线程操作的常用方法:

1. java如何实现一个多线程
(1) 继承Thread
start()方法开启一个线程
(2) 实现Runnable
run()方法执行线程内容
Thread(Runnable)
2. 线程常用方法
a) currentThread()
在线程Thread1中调用该方法返回当前线程名字是thread1,在main方法中运行 thread1.start() 再调用该方法返回当前线程名字是main。所以该方法主要用来获取当前运行的线程。
b) isAlive() 检测当前线程是否是存活状态,存活返回true,否则返回false。
c) getId() 获取调用方线程的线程id。
d) stop()
暴力停止停止一个线程的运行。jdk api 过期作废的方法不建议使用,次方法使用时抛出 java.lang.ThreadDeath,此异常不需要显示捕获。同时存在一些潜在的问题,强制停止线程可能使一些清理的工作得不到完成,另外一个情况就是对锁定的对象“解锁”会导致监视器数据不能同步处理完成,出现数据不一致。推荐使用interrupt异常法。
e) sleep(long)
调用该方法后线程会使当前线程停止运行long毫秒,直到long毫秒后线程继续执行后边的逻辑,sleep期间线程呈等待状态,当调用该sleep方法的方法或者代码块获得到 的情况下,线程呈同步阻塞状态,不会释放锁直到程序执行完成。
f) suspend()/resume()
suspend可以暂停线程,resume恢复线程。与stop一样,过期作废的方法。不推荐使用的原因----独占。使用不当时,极易对公共对象独占,其它线程获取不到公共同步对象,进而导致死锁。
g) yield()
放弃当前的CPU资源,放弃的时间不确定,可能刚刚放弃立马又获得到CPU时间片。在操作系统中线程优先级越高,CPU越优先执行。设置线程优先级可以使用setPriority(),在java中优先级被分为1~10,小于1或者大于10 抛出异常 throw new IllegalArgumentException()。
线程具有继承性,线程A启动线程B 则线程B拥有A同级的优先权。优先级高的线程总是大部分优先执行,但不代表全部会执行,优先级与代码执行的顺序无关,CPU会尽量将执行资源让给优先级较高的线程。
h) interrupt()/isInterrupted()/interrupted() 借鉴另外文档
i) wait()/wait(long)/notify()/notifyAll()
Object的方法,等待/通知,线程在执行该几个方法前,必须获得该对象的对象级别的锁,可以是同步方法或者同步代码块,如果没有锁则抛出IllegalMonitorStateException。调用wait()后线程进入等待状态,直到有线程调用了该对象监视器notify()/notifyAll() 对等待状态唤醒。wait(long) 方法在long毫秒后自动唤醒。wait与sleep不同的是sleep 不会释放锁,而wait会释放当前的线程所持有的对象锁。
在多线程中wait/notify 常被用于线程之间的通信。生产者与消费者之间的关系。
sleep与wait对比:
都可以使线程进入暂时停止的状态,sleep时间结束后自动唤醒,await时间结束后或者直到被notify后唤醒。
sleep睡眠期间不释放锁,wait释放
与interrupt结合使用都可以使用异常法停止线程,释放对象锁,但是wait的线程对象会进入线程等待池,等待被唤醒。
j) join()/join(long)
在很多情况下,主线程创建启动子线程后,如果子线程运行时间比较早,主线程先于子线程结束,如果主线程需要等待子线程处理完某些程序或者数据,主线程需要这样的逻辑或者数据时,就可以使用到join了。在主线程内调用thread.start()方法后调用thread.join()。则可以等到子线程结束后主线程执行结束。所以说join是等待线程结束。

join与interrupt相遇后,如果主线程被打断则抛出异常线程结束,此时子线程依然在运行。
join(long) 主线程只等待long毫秒,join基于wait实现,所以会释放锁。



1取得和设置线程的名称
MyThread my = new MyThread();
new Thread(my).start();   //系统自动设置线程名称
new Thread(my,"线程-A").start(); //手动设置线程名称
new Thread(my).start();  //系统自动设置线程名称

然后在main方法里面调用Thread.currentThread().getName()取得当前线程的名称。
得到的结果如下:Thread-0、线程-A、Thread-1


说明:
java至少启动两个线程,一个是main方法(main),一个是垃圾回收线程
2判断线程是否启动
isAlive方法判断
3线程的强制运行


使用join方法使线程强制运行,线程强制运行期间,其他线程无法运行。


4线程的休眠


Thread.sleep(500)



5中断线程

当一个线程运行时,另外一个线程可以直接通过interrupt()方法中断其运行状态


程序运行结果:
1、进入run方法
3、休眠被终止

从以上程序可以看出,一个线程启动之后进入了休眠状态,原本是要休眠10秒之后再继续执行,但是主方法在线程启动之后的两秒就将其中断,休眠一旦中断之后将执行catch中的代码,并利用里面的return语句返回程序调用处。
6后台线程

即使java进程结束了,后台线程依然会执行。要想设置后台线程,只需要在start方法之前加上thread.setDaemon(true)就行。
7线程的优先级

thread.setPriority(Thread.MIN_PRIORITY)//设置最低优先级
thread.setPriority(Thread.NORM_PRIORITY)//设置中等优先级
thread.setPriority(Thread.MAX_PRIORITY)//设置最高优先级

8线程的礼让

,可以使用yield()方法将一个线程的操作暂时让给其他线程执行。

五:同步和死锁

一个多线程的程序如果是通过Runnable接口实现的,则意味着类中的属性将被多个属性共享,那么这样一来就会造成一种问题,如果这多个线程要操作同一资源时就很有可能出现资源同步的问题。例如以下的买票程序,如果多个线程同时操作时就容易出现票数为负数的情况。

结果:
卖票:ticket = 1
卖票:ticket = 0
卖票:ticket = -1
如果要解决这个问题,则必须使用同步。所谓同步就是指多个操作在同一个时间段内只有有个线程进行,其他线程要等待此线程完成之后才可以继续执行。
如图:

将以上代码做出如下修改,就可以使票数不会出现负数了

使用同步代码块实现格式:
synchronized(同步对象){
需要同步的代码;
}

六:死锁

同步可以保证资源共享操作的正确性,但是过多的同步也会产生死锁。所谓的死锁,就是指两个线程都在等待对方先完成,造成程序的停滞,一般的死锁都是在程序运行时出现的

七:线程的生命周期


大部分线程生命周期前面已经讲过,下面说说这三个方法
1 suspend()方法:暂时挂起线程
2resume()方法:恢复挂起的线程
3stop()方法:停止线程

对于这三种方法并不推荐使用,因为这三种方法在操作时会产生死锁的问题。
那么如何停止线程运行呢?

一旦调用stop方法,就会将MyThread类中的flag变量设置为false,这样run()方法就会停止运行

终止线程的方法有三种:

(1)使用退出标志,使线程正常退出,也就是当run()方法完成之后线程终止

(2)使用Thread的interrupt()终止线程

(3)使用Thread的stop()方法强行终止线程(这个方法不推荐使用,因为stop()和suspend()、resume()方法一样,也可能发生不可预料的结果)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值