多线程就是实现多任务的一种方式,每个进程都有自己独立的一块内存空间,线程是指进程中的一个执行流程,一个进程中可以启动多个线程。
java中用java.lang.Thread类或者java.lang.Runnable接口来定义,实例化和启动新的线程
线程总体分为了两类:一是用户线程二是守候线程
(一)定义线程:
1.扩展java.lang.Thread类。这个类中有个run()方法。public void run()子类应该重写该方法。
2.实现java.lang.Runnable接口。void run()
(二)实例化线程:
1.如果是扩展java.lang.Thread类的线程,则直接new就可以了。
2.如果是实现了java.lang.Runnable接口的类,则用Thread的构方法。
(三)启动线程:在线程的Thread对象上调用start()方法,而不是run()或者别的方法。
调用start()方法之前线程是处于一个新的状态中,就是指有一个Thread对象但是还没有一个真正的线程。
在调用start()方法 之后会启动新的执行线程具有新的调用栈。新状态转移到了可执行的状态其目标 的run()方法会执行。
调用run方法是可以的但是不启动新的线程。
线程的状态可以分为五大状态:生,死,可运行,运行,等待/阻塞
1.新状态:线程对象这时候就其实已经创建了,但是还没有在上面调用start()方法。
2.可运行状态:这时候是这个线程有资格运行但是调度程序还没有将它选定为运行状态。后面可以返回到可运行的状态。
3.运行状态:这时候就是现成的调度程序从可运行池当中选择了一个线程作为当前线程时线程所处的状态。线程进入运行状态的唯一一种方式。
4.等待/阻塞/睡眠:这是侯线程它是活的,但是它没有运行的条件,即它是可运行的。(这三个都可以阻止线程的执行)
5.死亡状态:当线程的run()完成时就是认为它死去了。
睡眠:Thread.sleep(long millis) 和 Thread.sleep(long mills,int nanos)原因是线程执行太快了,或者是强制进行下一轮。
睡眠的实现就是调用静态的方法
try{
Thread.sleep(123);
}catch(InterruptedException e){
e.printStackTrace();
}
线程的睡眠是帮助所有线程获得运行机会的最好方法。
线程的睡眠到期会自动的苏醒,然后会返回到可运行的状态当中,不是运行的状态。sleep()中指定的时间是睡眠的最短时间不能保证睡眠到期之后就能开始执行。
sleep()是静态的方法,只能控制当前正在运行的线程。
线程的优先级和线程的让步yield():线程的让步是通过Thraed.yield()来实现的。yield方法的作用就是:暂停当前正在执行的线程对象,并且执行其他线程。
我感觉要理解yield()就必须要先了解线程的优先级的概念。
首先线程它是存在着优先级优先级大概在1~10之间。JVM的线程调度程序是基于优先级的抢先调度的机制。
当优先级相等的时候就是由JVM的调度程序自由的选择它喜欢的线程。一是选择一个线程运行,直到它阻塞或者运行完成为止。二是进行了时间的分片,为池内的每一个线程都提供均等的运行的机会。
通过setPriority(int newPriority)更改线程的优先级。
如:Thread t = new MyThread();
t.setPriority(8);
t.start();
Thread类中有三个常量,用来定义线程的优先级的范围:
static int MAX_PRIORITY 最高优先级
static int MIN_PRIORITY 最低优先级
static int NORM_PRIORITY 默认优先级(5)
Thread.yield()方法:暂停当前正在执行的线程的对象,并执行其他线程。但是yield()从将线程转到等待/睡眠/阻塞的状态,大多数的情况下yield()是将线程从
运行状态转到了可运行的状态中,但是可能会没有效果。不能保证太多。
join()方法Thread的非静态方法join()让一个线程加入到另外一个线程的尾部,在另外一个线程执行完毕之前这个线程不能工作。
Thread t = new MyThread();
t.start();
t.join();
join()方法还有带超时限制的重载版本,例如t.join(5000)则让线程等待5000毫秒,如果超过这个时间,则停止等待,变为可运行的状态。
线程加入join()对线程栈导致的结果就是线程栈发生了变化,栈中一个线程join后在一个线程的下面。
Java线程:线程同步与锁。
线程的同步就是为了防止多个线程访问一个数据对象的时候,对数据造成破坏。
下面是一个例子:
结果是明显不对的,这是因为两个线程不加控制的访问Foo对象并且修改其数据而导致的。要保证结果的合理性就要加以限制,每次这能有一个线程访问。
要使用synchronized关键字进行同步。(上面试在方法体加synchronized。即public synchronized void run())只能同步方法。
锁的原理:java中每个对象都会有一个内置锁,当程序运行到非静态的synchronized同步方法上时,自动获得当前实例有关的锁,一个对象只有一个锁,
所以一个线程获得一个锁,其他线程就不能获得,除非有锁的那个释放锁。释放锁就是锁线程退出了synchronized同步方法或代码块。线程
是可以获得多个锁的,一个对象的同步方法里调用另外一个对象的同步方法时获得。
不同对象上锁定的线程是互不干预的
静态方法锁定在Class上
非静态方法锁定在类的对象上
反正多个线程同事访问互斥的数据时应该同步以保护数据确保两个线程不会同事修改更改它。
但是同步是不一定线程安全的,解决方法就是每个方法加上synchronized关键字
线程的死锁:当两个线程被阻塞,每个线程都在等待另一个线程的时候就发生了死锁。
小结:
1.线程的同步是通过锁来实现的,每个对象有且仅有一个锁,这个锁与特定的对象关联。
一旦回去了对象锁,其他访问该对象的线程就无法再访问该对象的其他同步的方法。
2.静态同步方法,锁是针对这个类的,锁的对象是class对象,静态与非静态方法的锁互不干扰
3.对于同步关键是清醒在哪个对象上同步。
4.当多个线程等待一个对象锁的时候,没有获取到锁的线程将会发生阻塞。
5.死锁是线程之间互相等待锁造成的。