Java 多线程

进程是正在运行的程序。而线程是进程的执行路径(负责代码的执行)。

“利用对象,可将一个程序分割成相互独立的区域。我们通常也需要讲一个程序转换成多个独立运行的子任务,像这样的每一个子任务都叫做一个线程(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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//求输出

public class Text implements Runnable{
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(new Text());
t.start();
System.out.print("m1");
t.join();
System.out.print("m2");
}
public void run(){
System.out.print("r1");
System.out.print("r2");
}
}

调试输出是r1r2m1m2/m1r1r2m2/r1m1r2/m2 由于join()方法的调用 m2 一定是最后输出。

–wait for update