·什么是线程
线程:进程中负责程序执行的执行单元。一个进程中至少有一个线程。
多线程:一个进程中包含有多个线程,但CPU在同一时间只允许一个线程的进行。所以有多个线程的运行是根据CPU切换完成,如何切换由CPU决定,因此多线程运行具有不确定性。
·线程的创建方法
1.继承Thread类
即创建一个thread类,或者创建一个thread子类的对象
1>Thread类的介绍
-
Thread类是一个线程类,位于java.lang包下
-
Thread的主要构造方法:
1.1 thread() 创建一个线程对象
1.2 thread(String name) 创建一个具有指定名称的线程对象
1.3 thread(Runnable target) 创建一个基于runnable接口实现类的线程对象
1.4 thread(Runnable target,string name) 创建一个基于Runnable接口实现类,并且具有指定名称的线程对象
-
常用方法: 1.1 public void run() 线程相关的代码写在该方法中,一般需要重写
1.2 public void start() 启动线程
1.3 public static void sleep(long m) 线程休眠m毫秒
1.4 public final void join() 优先执行调用join方法的线程
2>具体的代码实现
package _Thread;
class mythread extends Thread{
//重写Run方法
public void run() {
System.out.println(getName()+"线程");
}
}
public class _threadTest {
public static void main(String[] args) {
mythread sr=new mythread();
sr.start;//启动线程
}
}
实现结果

然后为了将CPU时间片的轮转体现出来,将加入2个循环如下:
package _Thread;
class mythread extends Thread{
public void run() {
for(int i=1;i<=10;i++)
System.out.println(getName()+"线程"+i+"次");
}
}
public class _threadTest {
public static void main(String[] args) {
mythread sr=new mythread();
sr.start();
for(int i=1;i<=10;i++)
System.out.println("主线程"+i+"次");
}
}
两次不同的运行结果:


这说明线程的运行是有cpu随机切换完成的,具有不确定性。
tips:
线程只能启动一次
主方法也是一个线程
2.实现Runnable接口
1>Runnable类的介绍
由于JAVA是不支持多继承的,所以如果一个类已经有一个父类,那么我们就无法通过继承Threadl类来创建线程,此时我们就可以通过实现Runnable接口来完成。Runnable是java中实现线程的接口,它有且只有一个方法Run(),并且任何实现线程功能的类必须实现该接口。
2>具体的代码实现
package _Thread;
class mythread implements Runnable{
public void run() {
System.out.println(Thread.currentThread().getName()+"线程");
/*
由于这里没有继承Thread类中的方法,
所以这里调用Thread中的currentThread()的getName()来获取当前线程的名字
*/
}
}
public class _threadTest {
public static void main(String[] args) {
mythread sr=new mythread();
Thread pr=new Thread(sr);//通过调用Thread类中的含参构造方法来创建一个线程对象
pr.start();
}
}
tips:一个实例可以被多个线程所共享,一般用于多个线程处理同一个资源的情况。
·线程的状态和生命周期
1.线程的几种状态
1.1新建(NEW)状态
当程序使用new关键字创建了一个线程之后,该线程就处于新建状态.
1.2可运行(Runnable)状态
该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权 。
·关于直接调用run()方法
如果直接调用线程对象的run方法,也就是相当于启动线程,并直接执行run方法,而且再run方法结束之前无法运行其他线程。
1.3正在运行(Runnming)状态
当CPU开始调度处于可运行状态的线程时,此时线程获得了CPU的时间片才得以真正开始执行run()方法的线程执行体,则该线程处于可运行状态。
1.4阻塞(Blocked)状态
处于运行状态的线程在某些情况下,让出CPU并暂时停止该线程的运行,也就是进入了阻塞状态。
1.5终止(Dead)状态
线程的run()或call()方法执行完成,线程会正常结束;
线程抛出了一个未捕获的Exception或Errorl异常,线程会意外终止;
或者直接调用该线程stop()方法来结束该线程,但可能会导致线程的死锁;
2.线程的生命周期
线程的生命周期实际上是指线程的几种状态的之间的切换。
生命周期图如下:

2.1 public static native void sleep(long millis)
- 作用:使当前正在运行的线程休眠millis毫秒
- 由于该线程是从运行状态到阻塞状态,再到可运行状态,并不是直接到可运行状态。所以中间会产生一定的时间误差,使其略长于所设参数。
- 该方法是在Thread类中的一个静态方法。
2.2 object.wait()
- 作用:暂停该线程,释放对象锁
- 该方法是在object类中的方法
- 在调用前需要先拥有某对象的锁,所以一般在 synchronized 同步块中使用
·对象锁简单的理解就是在代码中的方法上加了synchronized的锁,或者synchronized(this)的代码段 于之对应的还有类锁,指在代码中的方法上加了static和synchronized的锁(在下面还会详细涉及到)。
2.3 Thread.yield()
- 作用:表示暂停当前线程,让出 CPU给优先级与当前线程相同,或者优先级比当前线程更高的就绪状态的线程。
- 需要注意的是这和 sleep() 方法不同的是,它不会进入到阻塞状态,而是直接进入到可运行状态。
2.4 Thread.join()
-
会优先将该线程运行完毕,再去调用其他线程,也就是等待该线程的终止
-
其重载方法还有
public final void join(long millis)等待该线程终止的时间最长为 millis 毫秒
public final void join(long millis, int nanos)等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒
·线程的优先级
Java 把线程优先级分成10个级别(依次为1到10,数字越大线程的优先级越高),线程被创建时如果没有明确声明则使用默认优先级(5)。虽然优先级越高越先被执行,但我们不能用线程优先级来规定线程的执行顺序。因为线程的执行顺序还受很多其他因素影响,例如操作系统和JVM的调度等等,所以我们只能说线程优先级越大,越先被执行的可能性越高。这里也体现了CPU调度的随机性。
1.三个优先级的常量
最小优先级(1)=Thread.MIN_PRIORITY
默认优先级(5)=Thread.NORM_PRIORITY
最大优先级(10)=Thread.MAX_PRIORITY
2.优先级的相关方法
2.1 public int getPriority()
- 作用:取得线程的优先级
2.2 public void setPriority(int newPriority)
- 作用:设置线程的优先级
·线程的同步
由于线程被执行的时间、占用cpu的时间都是不确定的,所以在有多个线程要被执行的情况下,会产生许多问题。例,在有两个线程(线程1和线程2)对同一个类的属性进行操作时,如果线程1对该属性进行“加”的操作前,线程2突然抢占了CPU来对该属性进行了“减”的操作,对该属性进行修改,那么就可能出现类似于1+1=1的结果。
为了避免上述结果的出现,这里需要调用一个synchronized关键字(同步),来对该共享对象进行上锁,即让该共享对象在同一个时刻,只能被一个线程访问。
synchronized关键字的使用
一般加在线程前,表明该线程不允许被打断, 可以加在静态方法/方法/语句块前。 例:
public synchronized void 方法名(){}
public static synchronized void 方法名(){}
synchronized(对象名){}
线程间的通信
我们先引进三个方法
wait()中断方法的执行,使线程等待
notify()随机唤醒等待的某一个线程
notifyall()唤醒所有处于等待的线程
对多线程的处理,下面以对2个线程的处理为例
一般是将公共对象当作一个容器queue,并在该容器内设置一个Boolean类型的值,在将线程1和2设置为synchronized类型后,在线程1中添加判断语句,如果该Boolean型值为ture(也可以是flase)才能执行该线程的操作语句,否则进入等待(在这里调用wait()使其暂停运行,记得要在线程最后改变Boolean类型值的值),对线程2的操作也类似,这样就能解决对线程调度顺序问题。
但这样问题并不是完全被解决,如果线程1和2都进入休眠状态,那么锁死,即两个线程都不进行。所以我们还需要在线程1和线程2的最后加上唤醒醒另一个线程的语句(这里调用notifyall(),之所以不调用notify()是因为它是随机唤醒某一个线程,如果在程序中还有线程3、线程4···,那我们想唤醒的线程有可能不被唤醒)