Java线程
文章目录
1.2 何谓线程
1.2.1 单线程程序
- 在单线程程序中,"在某一时间点执行的处理"只有一个,因为 “正在执行程序的主体” 只有一个。
- Java程序执行时,至少会有一个线程在运行,即主线程。
1.2.2. 多线程程序
- 启动新线程时调用的是
start
方法,而不是run
方法。当然run
方法是可以调用的,但调用它不会启动新的线程。 - 调用
start
方法后,程序会在后台启动新的线程,然后,由这个新线程调用run
方法。 - 输出结果显示,多线程情况下输出不一定按照代码顺序。
- 顺序、并行与并发:
- 顺序(sequential)用于表示多个操作“依次处理”。比如把十个操作交给一个人处理时,这个人要一个一个地按顺序来处理。
- 并行(parallel)用于表示多个操作“同时处理”。比如十个操作分给两个人处理时,这两个人就会并行来处理。
- 并发(concurrent)相对于顺序和并行来说比较抽象,用于表示 “将一个操作分割成多个部分并且允许无序处理”。比如将十个操作分成相对独立的两类,这样便能够开始并发处理了。如果一个人来处理,这个人就是顺序处理分开的并发操作,而如果是两个人,这两个人就可以并行处理同一个操作。
1.3 线程的启动
1.3.1 启动线程的方法
- 利用Thread类的子类的实例启动线程。
- 创建子类的实例并调用 start 方法
MyThread t = new MyThread();
t.start();
- 利用Runnable接口的实现类的实例启动线程。
- 创建接口实现类的实例,把它作为参数传给Thread的构造函数
Runnable r = new MyThread();
Thread t = new Thread(r);
t.start();
1.3.2 程序的终止
Java程序的终止是指除守护线程(Daemon Thread)以外的线程全部终止。守护线程是执行后台作业的线程。我们可以通过setDaemon方法把线程设置为守护线程。
1.3.3 Thread类和Runnable接口
Thread类本身还实现了Runnable接口,并且持有run方法,但Thread类的run方法是去调用子类的run方法。
1.4 线程的暂停
1.4.1 sleep方法的调用
- sleep方法的调用放在了try…catch中,这是因为,sleep方法有可能会抛出 InterruptedException 异常。
- 如果要中途唤醒被 Thread.sleep休眠的线程,则可以使用interrupt方法。
1.5 线程的互斥处理
1.5.1 synchronized方法
- 如果声明一个方法时,在前面加上关键字synchronized,那么这个方法就只能由一个线程运行。只能由一个线程运行是每次只能由一个线程运行的意思,并不是说仅能让某一特定线程运行。
- 如果有一个线程正在运行某个实例中的某个synchronized方法,则其他线程就无法运行该实例的所有synchronized方法,但非synchronized方法可以同时运行。
- 每个实例拥有一个独立的锁。因此,并不是说某一个实例中的synchronized方法正在执行中,其他实例中的synchronized方法就不可以运行了。
1.5.2 锁和监视
- 线程的互斥机制称为监视(monitor)。另外,获取锁有时也叫作“拥有(own)监视”或
“持有(hold)锁”。 - 当前线程是否已获取某一对象的锁可以通过Thread.holdsLock方法来确认。当前线程已获取对象obj的锁时,可使用assert来像下面这样表示出来。
assert Thread.holdsLock (obj);
- synchronized 实例方法是使用 this 的锁来互斥,synchronized 静态方法是使用该类的类对象的锁来互斥
1.6 线程的协作
1.6.1 线程控制方法
Java提供了用于执行线程控制的wait方法、notify方法和notifyAll方法。wait是让线程等待的方法,而notify和notifyAll是唤醒等待中的线程的方法。
1.6.2 等待队列
- 所有实例都拥有一个等待队列,它是在实例的wait方法执行后停止操作的线程的队列。打个比方来说,就是为每个实例准备的线程休息室。
- 在执行wait方法后,线程便会暂停操作,进入等待队列这个休息室。除非发生下列某一情况,否则线程会一直在等待队列中休眠。当下列任意一种情况发生时,线程便会退出等待队列:
- 有其他线程的 notify 方法来唤醒线程
- 有其他线程的 notifyAll 方法来唤醒线程
- 有其他线程的 interrupt 方法来唤醒线程
- wait方法超时
1.6.3 wait、notify、notifyAll
- 若要执行 wait、notify 和 notifyAll 方法,线程必须持有锁,否则会抛出 illegalMonitorstateException。但如果线程进入等待队列,便会释放其实例的锁。
- 假如在执行notify方法时,正在等待队列中等待的线程不止一个,对于 “这时该如何来选择线程” 这个问题规范中并没有作出规定。究竟是选择最先wait的线程,还是随机选择,或者采用其他方法要取决于Java平台运行环境。
- notify 方法仅唤醒一个线程,而 notifyAll 则唤醒所有线程,这是两者之间唯一的区别。
- wait、notify 和 notifyAll 都是 Object 类的方法,而非 Thread 独有。