- 线程启动两种方式:
- 利用Thread的子类的实例启动线程;
public class PrintThread extends Thread{
private String message;
public PrintThread(String message){
this.message = message;
}
@Override
public void run() {
int index = 0;
while(index<1000){
System.out.println(message);
index++;
}
}
public static void main(String[] args) {
new PrintThread("apple").start();
new PrintThread("orange").start();
}
}
- 实现Runnable接口的实例,作为new Thread()的构造参数;
public class Printer implements Runnable{
private String message;
public Printer(String message){
this.message = message;
}
@Override
public void run() {
int index = 0;
while(index<1000){
System.out.println(message);
index++;
}
}
public static void main(String[] args) {
new Thread(new Printer("apple")).start();
new Thread(new Printer("orange")).start();
}
}
-
启动线程是调用Thread实例的start()方法,而不是run()方法;
-
如果CPU只有一个,那么并发处理就是顺序执行的(各条线程分时间段切换占有CPU,所以实际效果是串行处理),而如果有多个CPU,那么并发处理就可能会并行运行;
-
多线程编程时,根据任务要求必须正确编写线程的互斥处理和同步处理;
-
程序正常运行时(除强制停止退出),直到所有的线程终止后,程序才会终止。
-
java.util.concurrent包中定义了一个创建线程抽象工厂接口ThreadFactory,隐藏了new Thread的代码。
Executors.defaultThreadFactory()返回ThreadFactory接口实现实例。
ThreadFactory threadFactory = Executors.defaultThreadFactory();
threadFactory.newThread(new Printer("apple")).start();
-
线程暂停:在线程中代码Thread.sleep(1000),Thread的静态方法。
-
数据竞争:在多条线程同时操作某个数据或者资源而引起的与预期相反的情况称为数据竞争或竞态条件。
-
java使用关键字synchronized来执行线程的互斥处理。
1)synchronized方法:(对象锁,该实例作为锁,即this)
public synchronized void deposit(int m){
//取款
}
注意:上图的两个synchronized方法共享一个锁!
每个实例都拥有一个独立的锁。因此,并不是说某一个实例中的synchronized方法正在执行中,而其他实例中的synchronized方法就不可以运行了。
2)synchronized代码块:
synchronized(this){
//业务代码
}
- 类锁,所有实现该类的实例都将互斥;
synchronized静态方法:
public static synchronized void deposit(int m)
synchronized代码块:
synchronized(Bank.class){
//业务代码
}
-
线程协作,场景:
普通队列(非线程安全队列),有多个读取队列线程,当队列中没有数据时,读取线程将等待;多个写入线程,当写入线程写入数据后,须通知等待的读取线程可以取数据了。 -
java线程协作可以通过 wait、notify、notifyAll实现。
wait是让线程等待的方法,而notify和notifyAll唤醒等待的线程。
这三个方法能得到执行的条件是,一、在线程中。 二、该线程获取到锁。
这三个方法都是Object类的方法。所以也是Thread的方法。 -
等待队列----线程休息室。所有实例都拥有一个等待队列,它存放调用wait方法后的线程队列。
-
当下列任意一种情况发生时,线程变回退出等待队列:
- 有其他线程的notify方法来唤醒线程。
- 有其他线程的notifyAll方法来唤醒线程。
- 有其他线程的interrupt方法来唤醒线程。
- wait方法超时。
-
执行wait , this.wait() 等于 wait() 。
-
若要执行wait方法,线程必须持有锁(这是规则),但如果线程进入等待队列,便会释放其实例的锁。
-
notify()只会唤醒等待队列中的一个线程退出队列。 线程想要进入wait的下一个操作,也须重新获取锁。并不是唤醒了,就可以接着执行wait的下一个操作,需要重新获取到实例锁。
-
一般来说,使用notifyAll()时的代码要比使用notify()时的代码要健壮。因为notify可能有漏掉唤醒的线程,导致该线程一直在休息室(WAITING)状态。
-
线程状态迁移,线程有如下状态:
1)NEW 创建线程的初始状态;
2)RUNNABLE 线程正在运行时的状态;
3)TERMINATED 线程终止状态。
4)WAITING、TIMED_WAITING 执行wait()方法后。
5)BLOCKED 阻塞状态(竞争获取锁时的状态,获到锁之前);
获取线程状态可以调用Thread类的getState方法。 -
BLOCKED和WAITING、TIMED_WAITING的区别:
Java文档官方定义----
BLOCKED状态是:“这种状态是指一个阻塞线程在等待monitor锁”
WAITING状态是:“一个线程在等待另一个线程执行一个动作时在这个状态”
TIMED_WAITING状态为:“一个线程在一个特定的等待时间内等待另一个线程完成一个动作会在这个状态”
Thread.sleep(休眠时间)和wait(超时时间)会进入TIMED_WAITING状态。