多线程总结
在学习编程的过程中,我觉得不止要获得课本的知识,更多的是通过学习技术知识提高解决问题的能力,这样我们才能走在最前方,更多Java学习,请登陆疯狂java官网。
1.重写线程Thread,Runnable运行的是run方法,当调用线程start()时,会运行其run方法,
也可以主动去调用,只不过不在同一个线程里。
当一个线程既继承了Thread又继承Runnable,其thread必须调用target.run()实现对runnable的方法调用。
2.Object:的三种线程方法 wait() ,notify() ,notifyAll()
在某个线程里执行某个对象锁的wait方法会暂停该线程的运行,在另外一条线程里利用该锁的notify方法
可以唤醒某个该对象锁所在的线程;当该对象锁在多个线程里wait的时候,可以使用notifyAll唤醒运行。
3.线程调度-休眠/优先级/让步/合并/守护线程
休眠。静态方法Thread.sleep();谁调用谁休眠
对于优先级高的线程获取CPU机率比较大,并不一定优先级低的运行不了线程的让步含义就是使当前运行着线程让出CPU资源,但是然给谁不知道,仅仅是让出,线程状态回到可运行状态。Thread.yield()
线程的合并的含义就是将几个并行线程的线程合并为一个单线程执行,应用场景是当一个线程必须等待另一个线程执行完毕才能执行时可以使用join方法。 调用某个线程的join(),该线程运行完才运行调用它的线程代码。
守护线程的特征是当setDaemon(boolean on)将该线程标记为守护线程或用户线程。
当正在运行的线程都是守护线程时,Java 虚拟机退出。
该方法必须在启动线程前调用。实际上:JRE判断程序是否执行结束的标准是所有的前台执线程行完毕了,而不管后台线程的状态
4.线程的同步-同步方法/同步块
在多个线程运行的时候,是否有竞争资源同时改动。应该把竞争资源设为私有,并提供修改方法。可以是对修改方法的同步,也可以对代码块同步。 同步使用synchronized关键字。
5.线程池-JAVA5 API
固定线程池:
//创建一个可重用固定线程数的线程池 ExecutorService pool =
Executors.newFixedThreadPool(2);
//创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
ExecutorService pool = Executors.newSingleThreadExecutor();
//当要加入的池的线程(或者任务)超过池最大尺寸时候,则入此线程池需要排队等待。一旦池中有线程完毕,则排队等待的某个线程会入池执行。 可变尺寸的线程池:
//创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时
将重用它们。 ExecutorService pool = Executors.newCachedThreadPool(); 延迟连接池:
//创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。 ScheduledExecutorService pool = Executors.newScheduledThreadPool(2); //pool.execute(t3); //使用延迟执行风格的方法 pool.schedule(t4, 10, TimeUnit.MILLISECONDS);
//单任务延迟线程池:
//ScheduledExecutorService pool =
Executors.newSingleThreadScheduledExecutor();
6.有返回值的线程-JAVA5 API
线程不再继承runnable借口,而是callable接口并重写call方法。通过线程池submit调用,返回的Future对象,通过get()方法取得返回的对象。
7.线程锁:-JAVA5 API
Lock类代替synchronized,在需要同步的代码块里调用lock(),unlock(). -ReentrantLock
ReadWriteLock 用于I/0读写。
-ReentrantReadWriteLock
8.-JAVA5 API
//阻塞队列:java.util.concurrent.BlockingQueue继承了Queue接口,且是固定个数的队列,当队列满了,需要继续添加,或者队列没有元素,需要取出时,队列阻塞。
//阻塞栈:对于阻塞栈,与阻塞队列相似。不同点在于栈是“后入先出”的结构。
java.util.concurrent.BlockingDeque
是JAVA6 API中新提出的一个类。
9.线程调度-JAVA5 API
通过Lock对象的newCondition()得到Condition对象,并调用其await();signalAll();等。对应了Object中wait();
notifyAll();
10.原子量-JAVA5 API
java.util.concurrent.atomic的使用需要Lock的配合。
11.障碍器-JAVA5 API
CyclicBarrier c=CyclicBarrier(等待子线程数, 等待完毕需要执行的Runnable);子线程需要调用c的await().来通知主线程已经完成了子任务。
疯狂Java培训专注软件开发培训,提升学员就业能力,重点提升实践动手能力。技术知识沉淀深厚的老师,让你感受Java的魅力,激发你对于编程的热爱,让你在半年的时间内掌握8-10万的代码量,掌握Java核心技术,成为真正的技术高手;通过大量全真企业项目疯狂训练,迅速积累项目经验。让你成为技能型的现代化高端人才,迅速获得高薪就业!时间不等人,赶紧联系我们吧!
第二篇:Java多线程总结
Java多线程总结
线程的四种状态:
就绪Runable——运行中Running——阻塞Blocked——死亡Dead(结束)
线程的状态转换图
线程在一定条件下,状态会发生变化。线程变化的状态转换图如下:
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、调整线程优先级:Java线程有优先级,优先级高的线程会获得较多的运行机会。 Java线程的优先级用整数表示,取值范围是1~10,Thread类有以下三个静态常量: static int MAX_PRIORITY 线程可以具有的最高优先级,取值为10。 static int MIN_PRIORITY 线程可以具有的最低优先级,取值为1。 static int NORM_PRIORITY 分配给线程的默认优先级,取值为5。
Thread类的setPriority()和getPriority()方法分别用来设置和获取线程的优先级。 每个线程都有默认的优先级。主线程的默认优先级为Thread.NORM_PRIORITY。
线程的优先级有继承关系,比如A线程中创建了B线程,那么B将和A具有相同的优先级。 JVM提供了10个线程优先级,但与常见的操作系统都不能很好的映射。如果希望程序能移植到各个操作系统中,应该仅仅使用Thread类有以下三个静态常量作为优先级,这样能保证同样的优先级采用了同样的调度方式。
2、线程睡眠:Thread.sleep(long millis)方法,使线程转到阻塞状态。millis参数设定睡眠的时间,以毫秒为单位。当睡眠结束后,就转为就绪(Runnable)状态。
3、线程等待:Object类中的wait()方法,导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法。这个两个唤醒方法也是Object类中的方法,行为等价于调用 wait(0) 一样。
4、线程让步:Thread.yield() 方法,暂停当前正在执行的线程对象,把执行机会让给相同或者更高优先级的线程。
5、线程加入:join()方法,等待其他线程终止。在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,直到另一个线程运行结束,当前线程再由阻塞转为就绪状态。
6、线程唤醒:Object类中的notify()方法,唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。 直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。类似的方法还有一个notifyAll(),唤醒在此对象监视器上等待的所有线程。
JAVA多线程实现的两种模型
区别与联系:
? Runnable是Thread的接口,在大多数情况下“推荐用接口的方式”生成线程,因为接口
可以实现多继承,况且Runnable只有一个run方法,很适合继承。
? 在使用Thread的时候只需要new一个实例出来,调用start()方法即可以启动一个线程。
Thread Test = new Thread();
Test.start();
? 在使用Runnable的时候需要先new一个实现Runnable的类的实例,之后,创建线程时
将线程与该实例关联(其本质是让该线程执行时调用该实例中的run方法)。
? 扩展Thread类实现
class MyThread extends Thread {
public MyThread (String threadName) {
super(threadName);
}
public void run() {
System.out.println(getName() + " 线程运行开始!");
for (int i = 0; i < 10; i++) {
System.out.println(i + " " + getName());
try {
sleep((int) Math.random() * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(getName() + " 线程运行结束!");
}
}
public class TestMyThread {
public static void main(String[] rags) {
System.out.println(Thread.currentThread().getName() + " 线程运行开始!"); new MyThread ("A").start();
new MyThread ("B").start();
System.out.println(Thread.currentThread().getName() + " 线程运行结束!"); }
}
运行结果:
main 线程运行开始!
main 线程运行结束!
A 线程运行开始!
0 A
1 A
B 线程运行开始!
2 A
0 B
3 A
4 A
1 B
5 A
6 A
7 A
8 A
9 A
A 线程运行结束!
2 B
3 B
4 B
5 B
6 B
7 B
8 B
9 B
B 线程运行结束!
说明:
程序启动运行main时候,java虚拟机启动一个进程,主线程main在main()调用时候被创建。随着调用MyThread的两个对象的start方法,另外两个线程也启动了,这样,整个应用就在多线程下运行。
在一个方法中调用Thread.currentThread().getName()方法,可以获取当前线程的名字。在mian方法中调用该方法,获取的是主线程的名字。
注意:start()方法的调用后并不是立即执行多线程代码,而是使得该线程变为可运行态(Runnable),什么时候运行是由操作系统决定的。从程序运行的结果可以发现,多线程程序是乱序执行。因此,只有乱序执行的代码才有必要设计为多线程。
Thread.sleep()方法调用目的是不让当前线程独自霸占该进程所获取的CPU资源,以留出一定时间给其他线程执行的机会。实际上所有的多线程代码执行顺序都是不确定的,每次执行的结果都是随机的。
? 实现java.lang.Runnable接口
public class TestMyThread implements Runnable {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName() + " 线程运行开始!"); TestMyThread test = new TestMyThread ();
Thread thread1 = new Thread(test);
Thread thread2 = new Thread(test);
thread1.start();
thread2.start();
System.out.println(Thread.currentThread().getName() + " 线程运行结束!"); }
public void run() {
System.out.println(Thread.currentThread().getName() + " 线程运行开始!"); for (int i = 0; i < 10; i++) {
System.out.println(i + " " + Thread.currentThread().getName()); try {
Thread.sleep((int) Math.random() * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " 线程运行结束!"); }
}
运行结果:
main 线程运行开始!
Thread-0 线程运行开始!
main 线程运行结束!
0 Thread-0
Thread-1 线程运行开始!
0 Thread-1
1 Thread-1
1 Thread-0
2 Thread-0
2 Thread-1
3 Thread-0
3 Thread-1
4 Thread-0
4 Thread-1
5 Thread-0
6 Thread-0
5 Thread-1
7 Thread-0
8 Thread-0
6 Thread-1
9 Thread-0
7 Thread-1
Thread-0 线程运行结束!
8 Thread-1
9 Thread-1
Thread-1 线程运行结束!
说明:
TestMitiThread1类通过实现Runnable接口,使得该类有了多线程类的特征。run()方法是多线程程序的一个约定。所有的多线程代码都在run方法里面。Thread类实际上也是实现了Runnable接口的类。
在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target) 构造出对象,然后调用Thread对象的start()方法来运行多线程代码。
实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。因此,不管是扩展Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的,熟悉Thread类的API是进行多线程编程的基础。
读解Thread类API
构造方法摘要
Thread(Runnable target) 分配新的 Thread 对象。
Thread(String name) 分配新的 Thread 对象。
方法摘要
static Thread currentThread()
说明:返回对当前正在执行的线程对象的引用。
void start()
说明:使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
void stop()
说明:已过时,该方法具有固有的不安全性。
void suspend()
说明:已过时。 该方法已经遭到反对,因为它具有固有的死锁倾向。
void resume()
说明:该方法已过时,它只与 suspend() 一起使用,但 suspend() 已经遭到反对,因为它具有死锁倾向。
static void sleep(long millis)
说明:在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)。
static void sleep(long millis, int nanos)
说明:在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行)。 static void yield()
说明:线程让权,暂停当前正在执行的线程对象,并执行其他线程。
void interrupt(): 说明:中断线程。
void join():
说明:等待其他线程终止。在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,直到另一个线程运行结束,当前线程再由阻塞转为就绪状态。
void run() 说明:线程方法运行。
String toString()
说明:返回该线程的字符串表示形式,包括线程名称、优先级和线程组。