线程是一个程序的多个执行路径,执行调度的单位,依托于进程存在。 线程不仅可以共享进程的内存,而且还拥有一个属于自己的内存空间,这段内存空间也叫做线程栈,是在建立线程时由系统分配的,主要用来保存线程内部所使用的数据,如线程执行函数中所定义的变量。
注意:Java中的多线程是一种抢占机制而不是分时机制。抢占机制指的是有多个线程处于可运行状态,但是只允许一个线程在运行,他们通过竞争的方式抢占CPU。
一、定义一个线程有两种方法:
1、继承java.lang.Thread类,重写run()方法,通过线程的start()方法被JVM自动运行。
public class ThreadTest extend Thread {
@Override
public void run() {
System.out.println("I‘m running!");
};
}
2、实现Runnable接口,实现run方法,作为线程Thread类的参数并通过start()方法调用。
public class ThreadTest implements Runnable {
public void run() {
System.out.println("I‘m running!");
};
}
二、线程的状态
1、新生状态(New):当一个线程的实例被创建即使用new关键字和Thread类或其子类创建一个线程对象后,此时该线程处于新生(new)状态,处于新生状态的线程有自己的内存空间,但该线程并没有运行,此时线程还不是活着的(not alive);
2、就绪状态(Runnable):通过调用线程实例的start()方法来启动线程使线程进入就绪状态(runnable);处于就绪状态的线程已经具备了运行条件,但还没有被分配到CPU即不一定会被立即执行,此时处于线程就绪队列; 此时线程是活着的(alive);
3、运行状态(Running):一旦获取CPU(被JVM选中),线程就进入运行(running)状态,线程的run()方法才开始被执行;在运行状态的线程执行自己的run()方法中的操作,直到调用其他的方法而终止、或者等待某种资源而阻塞、或者完成任务而死亡;如果在给定的时间片内没有执行结束,就会被系统给换下来回到线程的等待状态;此时线程是活着的(alive);
4、阻塞状态(Blocked):通过调用join()、sleep()、wait()或者资源被暂用使线程处于阻塞(blocked)状态;处于Blocking状态的线程仍然是活着的(alive)
5、死亡状态(Dead): 执行run()方法完毕或者异常退出后的状态。对于一个处于Dead状态的线程调用start()方法,会出现一个运行期(runtime exception)的异常;处于Dead状态的线程不是活着的(not alive)。
三、线程的常用属性
1、优先级(priority)
2、thread.sleep()/sleep(long millis)
3、Thread.yield()
4、thread.join()
5、object.wait()
6、object.notify()/notifyAll()
7、synchronized block
四、Thread synchronized
1、非static的synchronized方法
被锁定的是类的对象,在多线程中,这个对象不能被多个线程同时访问,但是所属类的其他对象的此方法不受限制;
如果一个对象有多个非static的同步方法,一旦进入其中一个,就锁定此对象,无法进入其他同步方法
2、static的synchronized方法
由于static的成员不属于类对象,而是属于Class,因此,这里锁定的是Class类,而不是对象,所以如果static和非static的同步方法共存,
没有冲突。但是如果两个static同步方法,无论是否是不同对象,都是互斥的,因为他们都属于同一个Class类。
3、synchronized同步块
synchronized块则是一种细粒度的并发控制,只会将块中的代码同步,位于方法内、synchronized块之外的其他代码是可以被多个线程同时访问到的。
注意:被synchronized保护的数据应该是私有的。
五、Lock锁
Lock的锁定是通过代码实现的,而 synchronized 是在 JVM 层面上实现的。
synchronized在锁定时如果方法块抛出异常,JVM 会自动将锁释放掉,不会因为出了异常没有释放锁造成线程死锁。但是 Lock 的话就享受不到 JVM 带来自动的功能,出现异常时必须在 finally 将锁释放掉,否则将会引起死锁。
在资源竞争不是很激烈的情况下,偶尔会有同步的情形下,synchronized是很合适的。原因在于,编译程序通常会尽可能的进行优化synchronize,另外可读性非常好,不管用没用过5.0多线程包的程序员都能理解。
ReentrantLock
ReentrantLock提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在资源竞争不激烈的情形下,性能稍微比synchronized差点点。但是当同步非常激烈的时候,synchronized的性能一下子能下降好几十倍。而ReentrantLock确还能维持常态。
Atomic
和上面的类似,不激烈情况下,性能比synchronized略逊,而激烈的时候,也能维持常态。激烈的时候,Atomic的性能会优于ReentrantLock一倍左右。但是其有一个缺点,就是只能同步一个值,一段代码中只能出现一个Atomic的变量,多于一个同步无效。因为他不能在多个Atomic之间同步。