多线程
线程的优势:提高CPU的利用率。劣势:CPU切换开销。
每个线程只能被start( )一次。直接调用run( )不会启动线程。另外,线程的执行顺序也与start( )无关。
线程的创建可以通过继承Thread类或实现Runnable接口完成。推荐通过实现Runnable接口的方式创建线程。原因有以下两点:
- java只支持单继承。
- 另外,在继承模式下需要一个static的共享变量,否则无法被多个线程实例共享。或者声明一个属性,以这个属性作为锁,此时仅能使用同步代码块而无法使用同步函数。
线程的状态转换
- 线程由新建状态进入就绪状态:start( );
- 线程由就绪状态进入运行状态:CPU分配时间片;
- 线程由运行状态进入阻塞状态:sleep( ),等锁,wait( ),join( );
- 线程由阻塞状态进入就绪状态:sleep时间到,得锁,notify( )/notifyAll( );
- 线程由运行状态进入死亡状态:线程运行结束。
线程间同步
线程安全问题:多个线程同时访问共享变量,造成脏读,重复写操作。要求:只有当一个线程操作共享数据完毕,其他线程才能操作。但是线程同步机制会导致效率变低,因为多个线程不能同时执行同步代码块(方法)。
同步代码块与同步方法:本质上均使用this作为锁(对象监视器)。
锁:获得锁的线程可以执行同步代码块和同步方法。通常使用Runnable对象本身(this)作为锁,也可以单独声明一个成员属性。如果使用了继承方式创建线程,则可以考虑使用类锁或静态属性。
锁的释放:以下情况会释放锁。
- 同步代码块执行完毕;
- 或break/return;
- error/exception;
- wait( )。注意,sleep( )和yield( )不会释放锁。
在默认情况下,线程会竞争CPU,因此无法规定线程的调度顺序。如果要求线程按照特定的顺序被调用,需要使用线程间通信,即wait/notify机制。注意,wait( ), notify( ), notifyAll( )只能在同步方法或代码块中被调用。wait( )会导致线程释放锁,在被唤醒并取得锁以后会接着wait( )以后的位置执行。因此,要注意恰当的使用notify( ),否则会导致所有线程均在wait( )。