Java多线程理解

转载请注明出处:http://blog.csdn.net/mr_liabill/article/details/45311707   来自《LiaBin的博客》

1. Java多线程状态

在网上随便搜索的一幅java多线程状态图


各个状态解释如下:

1. 新建状态(New):新创建了一个线程对象。
2. 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3. 运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4. 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、阻塞在对象等待池中:运行的线程执行wait()方法,JVM会把该线程放入对象等待池中。
(二)、阻塞在对象锁池中:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入对象锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5. 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。


重点注意事项;

1. sleep()和wait()方法的区别

对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的。

调用sleep()方法导致了程序暂停执行指定的时间,让出cpu给其他线程,此时线程进入Blocked态,其它线程可以获取CPU时间,该线程指定的时间到了又会自动恢复Runnable状态,此时同其它线程一起竞争CPU时间。同时注意在sleep时间内,如果该线程持有某个对象的锁,那么该线程不会释放对象锁,就是说所有其它在等待该对象锁的线程都没法执行。

而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待池中,此时该线程阻塞在对象等待池,也就说此时处于阻塞在对象锁池中的其它线程就能被调度获取CPU时间执行。只有针对此对象调用notify()/notifyall()方法后本线程才能进入对象的锁定池中准备,如果该对象其它对象在使用,那么该线程仍然阻塞在对象锁池中;否则进入runnable状态。

2. notify()和notifyall()的区别

官方文档解释:

notifyall():    Wakes up all threads that are waiting on this object's monitor. A thread waits on an object's monitor by calling one of the wait methods.

notify():        Wakes up a single thread that is waiting on this object'smonitor. If any threads are waiting on this object, one of them is chosen to be awakened

一目了然,总结的说notifyall唤醒所有阻塞在该对象等待池中的线程,而notify只是唤醒该对象等待池中的线程的一个,具体是哪个,看CPU的调度


2. 编程实例

1. 生产者消费者模型

package hello.demo;

public class ProduceConsume {
    private int maxNum = 100;
    private int currentNum = 0;

    public ProduceConsume(int maxNum) {
        this.maxNum = maxNum;
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ProduceConsume test = new ProduceConsume(100);
        MyConsumeThread ct1 = new MyConsumeThread("Consume1", test, 40);
        MyConsumeThread ct2 = new MyConsumeThread("Consume2", test, 60);
        MyConsumeThread ct3 = new MyConsumeThread("Consume3", test, 20);
        MyProduceThread pt1 = new MyProduceThread("Produce1", test, 10);
        MyProduceThread pt2 = new MyProduceThread("Produce2", test, 40);
        MyProduceThread pt3 = new MyProduceThread("Produce3", test, 5);
        MyProduceThread pt4 = new MyProduceThread("Produce4", test, 50);

        ct1.start();
        ct2.start();
        ct3.start();
        pt1.start();
        pt2.start();
        pt3.start();
        pt4.start();

    }

    // synchronized修饰普通方法,对象锁
    public synchronized void produce(int number) {
        while (currentNum + number > maxNum) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        currentNum += number;
        System.out.println(Thread.currentThread().getName()
                + "----had produced: " + number + " currentNum:" + currentNum);
        this.notifyAll();
    }

    public synchronized void consume(int number) {
        while (currentNum - number < 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        currentNum -= number;
        System.out.println(Thread.currentThread().getName()
                + "----had consumed: " + number + " currentNum:" + currentNum);
        this.notifyAll();
    }
}

class MyProduceThread extends Thread {
    private ProduceConsume test;
    private int number;

    public MyProduceThread(String name, ProduceConsume test, int number) {
        // TODO Auto-generated constructor stub
        super(name);
        this.number = number;
        this.test = test;
    }

    @Override
    public void run() {
        test.produce(number);

    }
}

class MyConsumeThread extends Thread {
    private ProduceConsume test;
    private int number;

    public MyConsumeThread(String name, ProduceConsume test, int number) {
        super(name);
        this.number = number;
        this.test = test;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        test.consume(number);
    }
}
输出结果:此时输出结果不确定

Produce1----had produced: 10 currentNum:10
Produce4----had produced: 50 currentNum:60
Produce3----had produced: 5 currentNum:65
Consume1----had consumed: 40 currentNum:25
Consume3----had consumed: 20 currentNum:5
Produce2----had produced: 40 currentNum:45

此时Consume2消费进程永远阻塞在对象的等待池中,因为最后currentNum:45<60,所以该线程进入对象的等待池,但是后来也没有生产者线程在生产所以不能消费。


2. 实现3个线程依次打印ABC,并且循环10次

import java.util.concurrent.atomic.AtomicInteger;

public class Test {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        AtomicInteger syncObj = new AtomicInteger();

        PrintThread pt1 = new PrintThread("A", 0, syncObj);
        PrintThread pt2 = new PrintThread("B", 1, syncObj);
        PrintThread pt3 = new PrintThread("C", 2, syncObj);
        pt1.start();
        pt2.start();
        pt3.start();
    }
}

class PrintThread extends Thread {
    private int flag;
    private AtomicInteger syncObj;
    private String name;
    private int count = 0;

    public PrintThread(String name, int flag, AtomicInteger syncObj) {
        this.flag = flag;
        this.syncObj = syncObj;
        this.name = name;
    }

    @Override
    public void run() {
        while (true) {
            // 线程进入该代码段,需要获取syncObj对象锁,如果此时没有持有syncObj对象锁则该线程进入该对象的锁池中
            synchronized (syncObj) {
                // 关键代码逻辑
                if (syncObj.get() % 3 == flag) {
                    System.out.print(name);
                    // 该线程执行一次打印任务即进入对象的等待池中,等待被唤醒
                    syncObj.set(syncObj.get() + 1);
                    count++;
                    syncObj.notifyAll();// 同时唤醒其它阻塞在该对象等待池中的线程
                    if (count == 10) {
                        break;
                    }
                } else {
                    try {
                        syncObj.wait();// 该线程进入对象的等待池中,等待调用syncObj.notifyAll()被唤醒。
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}
输出结果:ABCABCABCABCABCABCABCABCABCABC


常见问题

1. 最大并发数开多少合适

原则上,可以无限大。但是开线程对内存的消耗很大,所以一般使用线程池管理线程


2. 线程怎么停止

参考《如何正确停止java中的线程》  Asynctask的cancel其实是调用了Thread的interrupt方法终止线程

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java线程是Java语言中的一项非常重要的特性,它允许程序同时执行多个任务。多线程可以提高程序的并发性和性能,但是也带来了一些挑战,如线程安全、死锁、资源竞争等问题。 Java线程的实现方式有两种:继承Thread类和实现Runnable接口。继承Thread类需要重写run()方法,该方法中包含线程需要执行的代码。实现Runnable接口需要实现run()方法,但是需要将Runnable对象传递给Thread类的构造方法中。 Java线程的核心概念包括线程优先级、线程同步、线程通信、线程池等。线程优先级可以通过设置Thread类的setPriority()方法来进行设置,但是并不保证优先级高的线程一定会先执行。线程同步可以通过关键字synchronized来实现,它可以保证同一时刻只有一个线程可以访问共享资源。线程通信可以通过wait()、notify()、notifyAll()等方法来实现,它可以使线程之间进行协作。线程池可以通过Executor框架来实现,它可以实现线程的复用,减少线程创建和销毁的开销。 在使用Java线程时,需要避免一些常见的问题,如死锁、资源竞争、线程安全等。死锁会导致线程之间相互等待,无法进行下去;资源竞争会导致多个线程同时访问共享资源,可能会导致数据的不一致;线程安全问题会导致多个线程同时访问共享资源,可能会导致数据的不一致或者程序崩溃等问题。 综上所述,Java线程是一项非常重要的特性,它可以提高程序的并发性和性能,但是在使用时需要注意一些常见的问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值