Java多线程&&守护线程&&管程法
1.守护线程
java中提供两种进程,守护进程和用户进程。
守护进程也叫做“后台进程”,“服务进程”,与用户进程最大的区别在于如果用户进程已经全部退出运行,只剩下守护进程存在,那么java虚拟机就会退出,即只要用户进程结束,不管此时守护进程是否已经结束,虚拟机都会结束。
public class MyTest {
public static void main(String[] args) {
God god = new God();
Thread godThread = new Thread(god);
godThread.setDaemon(true);//必须在线程开启之前,开启守护线程
godThread.start();
You you = new You();
new Thread(you).start();
}
}
class God implements Runnable{
@Override
public void run() {
for (;true;) {
System.out.println("God bless you forever");
}
}
}
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 3000; i++) {
System.out.println("you are alive");
}
System.out.println("再见");
}
}
2.生产者和消费者问题
生产者消费者问题是研究多线程程序时绕不开的经典问题之一,它描述是有一块缓冲区作为仓库,生产者可以将产品放入仓库,消费者则可以从仓库中取走产品。解决生产者/消费者问题的方法可分为两类:(1)采用某种机制保护生产者和消费者之间的同步;(2)在生产者和消费者之间建立一个管道。第一种方式有较高的效率,并且易于实现,代码的可控制性较好,属于常用的模式。第二种管道缓冲区不易控制,被传输数据对象不易于封装等,实用性不强。因此本文只介绍同步机制实现的生产者/消费者问题。
同步问题核心在于:如何保证同一资源被多个线程并发访问时的完整性。常用的同步方法是采用信号或加锁机制,保证资源在任意时刻至多被一个线程访问。Java语言在多线程编程上实现了完全对象化,提供了对同步机制的良好支持。在Java中一共有四种方法支持同步,其中前三个是同步方法,一个是管道方法。
(1)wait() / notify()方法
(2)await() / signal()方法
(3)BlockingQueue阻塞队列方法
(4)PipedInputStream / PipedOutputStream
本文只介绍最常用的前三种,第四种暂不做讨论,有兴趣的读者可以自己去网上找答案。
一、wait() / notify()方法
wait() / nofity()方法是基类Object的两个方法,也就意味着所有Java类都会拥有这两个方法,这样,我们就可以为任何对象实现同步机制。
wait()方法:当缓冲区已满/空时,生产者/消费者线程停止自己的执行,放弃锁,使自己处于等等状态,让其他线程执行。
notify()方法:当生产者/消费者向缓冲区放入/取出一个产品时,向其他等待的线程发出可执行的通知,同时放弃锁,使自己处于等待状态。
notify和notifyAll的区别
如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。
当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。
注意:
永远在synchronized的函数或对象里使用wait、notify和notifyAll,不然Java虚拟机会生成 IllegalMonitorStateException。
综上,所谓唤醒线程,另一种解释可以说是将线程由等待池移动到锁池,notifyAll调用后,会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待锁被释放后再次参与竞争。而notify只会唤醒一个线程。
public class MyTest2 {
public static void main(String[] args) {
Container container = new Container();
Productor productor = new Productor(container);
Consumer consumer = new Consumer(container);
productor.start();
consumer.start();
}
}
class Productor extends Thread{
Container container;
public Productor(Container container){
this.container=container;
}
@Override
public void run() {
for (int i = 1; i < 100; i++) {
container.push(new Chicken(i));
System.out.println("生产者生产了"+i+"只鸡");
}
}
}
class Consumer extends Thread{
Container container;
public Consumer(Container container){
this.container=container;
}
@Override
public void run() {
for (int i = 1; i < 100; i++) {
Chicken pop = container.pop();
System.out.println("消费者消费了"+pop.id+"只鸡");
}
}
}
//定义一个容器
class Container{
//容器
Chicken[] chickens=new Chicken[10];
int num=0;//用来计数
public synchronized void push(Chicken chicken){
//假如已经满了,等待消费者消费
if (num>=chickens.length){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
chickens[num]=chicken;
num++;
this.notifyAll();//通知消费者消费
}
public synchronized Chicken pop(){
//假如容器空的,等待
if (num<=0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num--;
Chicken chicken=chickens[num];
this.notifyAll();
return chicken;
}
}
//产品 鸡
class Chicken{
int id;
public Chicken(int id){
this.id=id;
}
}
输出结果:
生产者生产了1只鸡
生产者生产了2只鸡
生产者生产了3只鸡
生产者生产了4只鸡
生产者生产了5只鸡
生产者生产了6只鸡
生产者生产了7只鸡
生产者生产了8只鸡
生产者生产了9只鸡
生产者生产了10只鸡
生产者生产了11只鸡
消费者消费了10只鸡
消费者消费了11只鸡
生产者生产了12只鸡
消费者消费了12只鸡
生产者生产了13只鸡
...........
3.Java多线程优先级设置与获取
线程的优先级是指,线程的优先级越高越有可能执行,但仅仅是有可能而已,
.设置优先级
public final void setPriority(int newPriority)
.获取优先级
public final int getPriority()
对于优先级设置的内容可以通过Thread类的几个常量来决定:
最高优先级:public final static int MAX_PRIORITY = 10;
中等优先级:public final static int NORM_PRIORITY = 5;
最低优先级:public final static int MIN_PRIORITY = 1;
public class MyTest {
public static void main(String[] args) {
Priority priority = new Priority();
Thread a = new Thread(priority, "A");
Thread b = new Thread(priority, "B");
Thread c = new Thread(priority, "C");
a.setPriority(Thread.MIN_PRIORITY);
b.setPriority(Thread.MAX_PRIORITY);
c.setPriority(Thread.NORM_PRIORITY);
a.start();
b.start();
c.start();
}
}
class Priority implements Runnable{
@Override
public void run() {
int i=3;
for (;i>0;i--){
System.out.println(Thread.currentThread().getName());
}
}
Thread.yield()
让出CPU的使用权,给其他线程执行机会、让同等优先权的线程运行(但并不保证当前线程会被JVM再次调度、使该线程重新进入Running状态),如果没有同等优先权的线程,那么yield()方法将不会起作用。而执行 yield() 方法后转入就绪(ready)状态,yield() 方法(跟操作系统相关),移植性差一些
thread.join()
使用该方法的线程会在此之间执行完毕后再往下继续执行。
4.利用标志位,使用wait()和notifyAll()
public class MyTset2 {
public static void main(String[] args) {
Tv1 tv = new Tv1();
new Actor(tv).start();
new Watcher(tv).start();
}
}
class Actor extends Thread{
Tv1 tv;
public Actor(Tv1 tv){
this.tv=tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if (i%2==0){
this.tv.play("正在播放NBA总局赛");
System.out.println();
}else {
this.tv.play("勇士总冠军,杜兰特FMVP");
}
}
}
}
class Watcher extends Thread{
Tv1 tv;
public Watcher(Tv1 tv){
this.tv=tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
tv.watch();
}
}
}
class Tv1{
boolean flag=true;
String voice;
public synchronized void play(String voice){
if (!flag){
//演员等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("表演了"+voice);
this.voice=voice;
this.notifyAll();//让观众观看
this.flag=!this.flag;
}
public synchronized void watch(){
if (flag){
try {
this.wait();//观众的等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观众观看了"+voice);
this.notifyAll();
this.flag=!this.flag;
}
输出结果:
表演了正在播放NBA总局赛
观众观看了正在播放NBA总局赛
表演了勇士总冠军,杜兰特FMVP
观众观看了勇士总冠军,杜兰特FMVP
表演了正在播放NBA总局赛
观众观看了正在播放NBA总局赛
表演了勇士总冠军,杜兰特FMVP
观众观看了勇士总冠军,杜兰特FMVP
表演了正在播放NBA总局赛
观众观看了正在播放NBA总局赛
表演了勇士总冠军,杜兰特FMVP
观众观看了勇士总冠军,杜兰特FMVP
表演了正在播放NBA总局赛
....................