一、线程的创建(有四种方式)
1.继承Thread类
创建一个线程的第一个方式是创建一个新的类,该类继承 Thread 类,然后创建一个该类的实例。继承类必须重写 run() 方法,该方法是新线程的入口点。它也必须调用 start() 方法才能进入就绪态(Thread类的对象调用start()方法,则自动执行重写的run()方法,Thread类的线程都用这个方法来调用run()方法)。

public class MyThread extends Thread {
	public MyThread() {
		
	}
	public void run() {
		for(int i=0;i<10;i++) {
			System.out.println(Thread.currentThread()+":"+i);
		}
	}
	public static void main(String[] args) {
		MyThread mThread1=new MyThread();
		MyThread mThread2=new MyThread();
		MyThread myThread3=new MyThread();
		mThread1.start();
		mThread2.start();
		myThread3.start();
	}
}

可以看出,上面的start方法会自动进去就绪态,在进去运行态后会自动调用run方法。同时需要注意的是,继承自Thread的自带的start方法可以被改写,但是无论怎么改写,哪怕改写的方法里面没有写调用run()方法,start方法仍然会调用run()方法。看下面的例子,下面的例子重写了start方法,虽然没有显示地写调用run()方法,但是运行时run()还是会被调用。所以这里重写start()方法类似于调用前增强的概念,就是自己改写的start()方法会在执行完后,自动完成原本的start()方法要干的事情——调用run()。

class ThreadDemo extends Thread {
   private Thread t;
   private String threadName;
   
   ThreadDemo( String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );
      try {
         for(int i = 4; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);
            // 让线程睡眠一会
            Thread.sleep(50);
         }
      }catch (InterruptedException e) {
         System.out.println("Thread " +  threadName + " interrupted.");
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }
   
   public void start () {
      System.out.println("Starting " +  threadName );
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}
 
public class TestThread {
 
   public static void main(String args[]) {
      ThreadDemo T1 = new ThreadDemo( "Thread-1");
      T1.start();
      
      ThreadDemo T2 = new ThreadDemo( "Thread-2");
      T2.start();
   }   
}

补充:Thread类方法
1 public void start()使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
2 public void run()如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
3 public final void setName(String name)改变线程名称,使之与参数 name 相同。
4 public final void setPriority(int priority) 更改线程的优先级。
5 public final void setDaemon(boolean on)将该线程标记为守护线程或用户线程。
6 public final void join(long millisec)等待该线程终止的时间最长为 millis 毫秒。
7 public void interrupt()中断线程。
8 public final boolean isAlive()测试线程是否处于活动状态。
测试线程是否处于活动状态。 上述方法是被Thread对象调用的。

下面的方法是Thread类的静态方法。
1 public static void yield()暂停当前正在执行的线程对象,并执行其他线程。
2 public static void sleep(long millisec)在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
3 public static boolean holdsLock(Object x)当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。
4 public static Thread currentThread()返回对当前正在执行的线程对象的引用。
5 public static void dumpStack()将当前线程的堆栈跟踪打印至标准错误流。

2.实现Runnable接口
创建一个线程,第二个方法是创建一个实现 Runnable 接口的类。这也是最简单的创建线程的方法,也是推荐的方法(因为实现了Runnable接口后,还可以继承其他的类,但是继承Thread类的方法会让类无法再继承其他的类)。需要注意的是,Thread类也是实现Runnable接口的,同时具体写时,要先写一个类实现Runnable接口,也是重写run()方法,然后要把实现了Runnable接口的类作为Thread类的构造函数的形参传递给Thread类,再用这个Thread类来调用Start()方法。
需要注意的是,构建完Runnable类的实现类的实例对象后,要把这个实例作为参数来创建Thread对象,这个对象才是真正的线程对象,前面的所有工作都是为了创建这个对象。

public class MyThread implements Runnable{
	public static int count=20;
	public void run() {//也是重写run方法
		while(count>0) {
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"-当前剩余票数:"+count--);
		}
	}
	public static void main(String[] args) {
		MyThread Thread1=new MyThread();//生成实现Runnable类的对象
           //将实现Runnable接口的类的对象和线程名作为参数传给Thread
           //创建真正的线程对象Thread实例
		Thread mThread1=new Thread(Thread1,"线程1");
		Thread mThread2=new Thread(Thread1,"线程2");
		Thread mThread3=new Thread(Thread1,"线程3");
           //通过Thread的start()方法来运行线程
		mThread1.start();
		mThread2.start();
		myThread3.start();
	}

3.使用Callable和FutureTask创建线程
(1)创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。
(2)使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值
(3)使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)
(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class MyThread implements Callable<String> {
	private int count = 20;
	@Override
	public String call() throws Exception {
		for (int i = count; i > 0; i--) {
//			Thread.yield();
			System.out.println(Thread.currentThread().getName()+"当前票数:" + i);
		}
		return "sale out";
	} 
 
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		//创建Callable对象
           Callable<String> callable  =new MyThread();
           //创建FutureTask类包装Callable对象
		FutureTask <String>futureTask=new FutureTask<>(callable);
           
           //使用FutureTask对象来生成Thread类对象
		Thread mThread=new Thread(futureTask);
		Thread mThread2=new Thread(futureTask);
		Thread mThread3=new Thread(futureTask);

		mThread.start();
		mThread2.start();
		mThread3.start();
		System.out.println(futureTask.get());
		
	}

4.通过线程池启动多线程
在线程池一节详解

二、线程的睡眠和暂停
线程的暂停和停止之前有stop,resume和suspend关键字,不过现在已经启用,因为stop会导致线程不安全,suspend会导致死锁等问题。

1.线程的睡眠(静态方法Thread.sleep())
Thread类提供了静态方法Thread.sleep(a),来让进程睡眠a毫秒后再运行。

public class MyYieldStudy {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println(end-start + "毫秒过去了");
    }

Thread.sleep()注意点:

  1. sleep()不会释放锁,线程在sleep时仍然控制着锁,不释放锁;但是会释放它所占用的CPU资源。
  2. sleep()必须要捕捉异常,写sleep()必须要写try…catch
  3. 线程的暂停(静态方法Thread.yield())
    Thread类提供了静态方法Thread.yield(),用来暂停当前的线程,去运行其他的线程(其他鲜线程只能是其他的任务,不能是和yield线程相同的任务,因为yield没有交出锁)。
    yield()与sleep类似,只是不能指定暂停的具体时间,而且只能让同等优先级的有执行机会
public class YiledTest implements Runnable {
    private String name;

    public YiledTest(String name) {
         = name;
    }

    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(name  + ":" + i);
            if ( i == 5) {
                System.out.println(name  + ":" + i +"......yield.............");
                Thread.yield();//在i=5的时候,暂停去运行其他线程,然后再回来
            }
        }
    }

Thread.yield()注意点:和Threa.sleep()一样,Thread.yield()不会释放锁,但会释放占用的CPU资源。
只有Object.wait()会释放锁资源。

  1. 线程的终止
    线程的run()运行完,会自动终止。如果想要终止运行中的线程,有以下两种方法:
    (1)使用标记位,来使线程终止:
    下面的方法,使用flag作为线程的循环标记位,在main()函数中手动设置flag的值来终止线程。
public class Mythread9 implements Runnable {
    //设置标志位
    private volatile boolean flag = true;
    @Override
    public void run() {
        int i = 0;
        while (flag) {
            try {
                Thread.sleep(1000);
                i++;
                System.out.println("线程:" + Thread.currentThread().getName() + "第" + i + "次执行");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}



public class StopTest1 {
    public static void main(String[] args) throws InterruptedException {
        Mythread9 mythread9=new Mythread9();
        Thread thread=new Thread(mythread9,"子线程");
         thread.start();
         Thread.sleep(2000);
         mythread9.setFlag(false);//设置标志位,终止线程
    }

结果:
线程:子线程第1次执行
线程:子线程第2次执行

(2)使用Thread类的实例方法interupt()来终止线程
调用线程类的interrupt方法,其本质只是设置该线程的中断标志,将中断标志设置为true,并根据线程状态决定是否抛出异常。因此,通过interrupted方法真正实现线程的中断原理是:开发人员根据中断标志的具体值,来决定如何退出线程。
实际上,调用interrupt()后,这个线程的中断标志位会改变,使用isinterrupted可以检测到这个标志位,那么就是用isinterrupted()来当标志位,检测到就退出线程。(这么处理是一种退出方式,也可以检测到标志位后怎么怎么滴都可以)。
需要注意的是,interrupt()函数是另一个线程调用,来终端这个线程。比如在线程2中调用thread1.interrupt(),那么线程1就会被中断。一般示例代码都是在main()主线程中调用thread1.interrupt()来中断线程1.

public class MyThread10 implements Runnable {
                    private boolean flag = true;
                    @Override
                    public void run() {
                        int i = 1;
        while (flag) {
                try {
                      Thread.sleep(1000);
                      boolean bool = Thread.currentThread().isInterrupted();
                    if (bool) {//调用interrupt后,bool会变成true;
                        System.out.println("非阻塞情况下执行该操作。。。线程状态" + bool);
                            break;
                        }
                        System.out.println("第"+i+"次执行,线程名称为:"+Thread.currentThread().getName());
                        i++;
            } catch (InterruptedException e) {//捕获interrupted异常
                System.out.println("退出了");
                boolean bool = Thread.currentThread().isInterrupted();
                System.out.println(bool);
                return;
            }
        }
    }
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
    


public class StopTest1 {
    public static void main(String[] args) throws InterruptedException {
        MyThread10 mythread=new MyThread10();
        Thread thread=new Thread(mythread,"子线程");
         thread.start();
         Thread.sleep(1000);
        //方法interrupt()只是给受阻塞的线程发出一个中断信号,这样受阻线程就得以退出阻塞的状态。
        thread.interrupt();
    }
}

结果
第1次执行,线程名称为:子线程
退出了
false