进程是正在运行的程序。而线程是进程的执行路径(负责代码的执行)。
“利用对象,可将一个程序分割成相互独立的区域。我们通常也需要讲一个程序转换成多个独立运行的子任务,像这样的每一个子任务都叫做一个线程(Thread)”
– Think in Java
每个 Java 程序都至少有两个线程独立运行:main 线程以及 Java 的垃圾回收线程。
一、创建自定义线程
实际使用中需要我们自定义线程来实现多线程,通常有以下两种方法:
A. extends Thread 类
第一种方法有三个步骤来创建线程
1.自定义一个类来继承 Thread 类。
2.重写 Thread 类中的run() 方法。
3.创建 Thread 类子类的对象,并且调用start()方法开启线程。
注意事项有两条:一是将自定义线程的任务代码写在run()方法中,就像主线程的任务代码就是main方法中的所有代码。二是一定要用start()方法来开启线程,其会自动调用run()方法中的代码,如果直接调用run()方法,那么将不会开启一个新的线程。
B. implements Runnable 接口
1.自定义一个类来实现 Runnable 接口。
2.实现接口中的 run() 方法。
3.创建实现类的对象。
4.创建 Thread 类的对象并把 Runnable 实现类的对象作为实参传递。
5.调用 Thread 的 start() 方法开启线程。
Runnable 实现类的对象并不是一个线程对象,只有 Thread 或Thread 的子类才是线程对象。(因为没有 run() 方法)。
实际使用中多使用B方案,因为Java的单继承机制。一旦继承了 Thread 类便无法再继承其他类。
二、线程安全问题
由于CPU资源调度的随机性,多线程任务会出现线程安全问题。sun公司提供了线程同步机制来解决线程安全问题。
主要有同步代码块和同步函数两种方式。
A.同步代码块:
synchronized(“锁对象”){同步代码};
其中 锁对象可以是任意的 static 对象。既可以是声明的
static Object lock = new Object();
也可以是在字符串常量区的一个任意字符串
“lock”;
一个线程进入了同步代码块中时,锁对象自动变为 LOCK 模式,在该线程没有执行完同步代码块之前,不会允许任何线程进入同步代码块中执行代码。
B.同步函数:
synchronized 修饰的函数称为同步函数。其原理与同步代码块相似,非静态的同步函数的锁对象是 this 对象(函数的调用者),静态函数的锁对象是当前函数所属的类字节码文件(class)。
同步机制解决了线程安全问题,但也同时引发了死锁现象:
存在两个或以上的线程/并且存在两个或以上的共享资源时,线程互相等待资源。
实际使用中多使用A方案(lock对象比较清晰简单)。
public class MyText006{
public static void main(String[] args){
SellTicket sell1 = new SellTicket("sell1");
SellTicket sell2 = new SellTicket("sell2");
SellTicket sell3 = new SellTicket("sell3");
sell1.start();
sell2.start();
sell3.start();
}
}
class SellTicket extends Thread{
static int num = 500;
static Object o = new Object();
public SellTicket(String name){
super(name);
}
public void run(){
synchronized(o){
while( true){
if (num >= 0) {
System.out.println(Thread.currentThread().getName() +" 还剩下" +num +"张票");
num--;
}
else {
System.out.println("No Ticket!");
break;
}
}
}
}
}
三、线程的一些常用方法
sleep()方法: 静态方法,A线程执行A线程 sleep。不一定是调用者、
currentThread()方法:返回当前线程对象。 A线程执行A线程返回。
getPriority():返回线程的优先级。(默认为5,Java线程数字越大优先级越高,1~10)
setPriority(): 设置线程的优先级。
join(): :“等待该线程终止”,这里需要理解的就是该线程是指的主线程等待子线程的终止。也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行。
##线程通讯方法:
wait(); 等待
notify(); 唤醒
这两个方法属于Object对象,只能在同步代码块中使用,由锁对象调用。一旦执行wait()方法将会释放锁对象。
四、网易2014校招笔试题
1 | //求输出 |
调试输出是r1r2m1m2/m1r1r2m2/r1m1r2/m2 由于join()方法的调用 m2 一定是最后输出。
–wait for update