目录
线程概念
- 计算机中的每一个程序都可以看作是一个进程,在一个进程中还可以有多个执行单元同时执行,这些执行单元可以看做程序执行的一个个线索,被称为线程。
线程的几种状态
- 可以通过调用线程的getState()方法查看线程的状态
- New(新创建)
- 当创建一个进程,例如new Thread(Object obj),该线程还没有开始运行,它的状态为new
- Runnable(可运行:就绪)
- 当调用了线程的start()方式,线程的状态将变为Runnable(可运行状态)。一个处于可运行状态的进程可能在运行,也可能没在运行。JVM采用的处理机分配机制为抢占式调度,当线程分配到处理机时为正在运行;当处理机分配给线程的时间片结束之后,线程没有在运行。
- Blocked(阻塞)
- Waiting(等待)
- Time waiting(计时等待)
- Terminated(终止)
- 线程有两种可能进入终止状态
- run方法运行完毕,正常退出
- 因为一个没有捕获的异常终止了run方法
- 线程有两种可能进入终止状态
- 线程之间的转换
线程创建的两种方式
- 继承Thread类
-
package czbk; /** * @author Xxx * @Description: 多线程之继承Thread类 * @date 2018/11/18上午11:18 **/ public class Example02 { public static void main(String[] args) { Mythead mythead = new Mythead(); // 获取线程状态(New:新创建) System.out.println(mythead.getState()); // 启动线程 mythead.start(); // 取线程状态(Runnable:可运行) System.out.println(mythead.getState()); while(true){ System.out.println("main()方法正在执行"); } } } class Mythead extends Thread{ // 重写run方法 @Override public void run() { while(true){ // 取线程状态(Runnable:可运行) System.out.println(Thread.currentThread().getState()); System.out.println("Mythread类的run()方法正在执行"); } } }
- 实现Runnable接口
-
package czbk; /** * @author Xxx * @Description: 通过实现Runnable接口创建线程 * @date 2018/11/18上午11:35 **/ public class Example03 { public static void main(String[] args) { Mythead2 mythead2 = new Mythead2(); Thread thread = new Thread(mythead2); thread.start(); while(true){ System.out.println("main方法开始执行"); } } } class Mythead2 implements Runnable{ /** * 当一个对象实现了Runnable接口被用做创建一个线程, * When an object implementing interface <code>Runnable</code> is used * 启动这个线程会导致对象的run方法在单独的线程中被调用 * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see Thread#run() */ @Override public void run() { while(true){ System.out.println("Mythead2的run()方法开始执行"); } } }
线程优先级
- 在Java程序设计中,每一个线程都有一个优先级,调度器会优先调度优先级比较高的线程。一般情况下,线程会继承他的父进程的优先级。优先级分为1-10是个层级,值越大,优先级越高。可以通过调用setPriority()方法设定线程的优先级。
线程让步
- static void yield()
- 执行yield方法后,线程被处理机调度时执行yield()方法,则该线程将会放弃本次调度,进入就绪状态(Runnable)。
线程休眠
- static void sleep(long millis)
- 执行yield方法后,线程被处理机调度时执行yield()方法,则该线程将会放弃本次调度,进入阻塞状态(Blocked),阻塞时长为millis毫秒。
线程插队
- final void join()
- 线程A在被调度时,调用线程B的join()方法,将使处理机调度线程B,只有线程B执行完毕后,处理机才会调度线程A
线程同步
- 多个线程共享对同一数据的存取,若不加以控制,将使资源的状态发生意想不到的错误。
- 例如,银行对一笔钱进行操作时,线程A对金额进行了更改,在A准备将金额写入数据库时,处理机调度了线程B,B又对金额进行了更改并写入数据库,然后处理机调度线程A,A将此时的金额写入数据库。最后得到的结构并不是人们想要的,因为B抹去了A对金额的操作。之所以发生这样的情况是因为线程对金额的操作并不是原子性并且线程的调度具有异步性。
- Java提供了两种机制方式代码块受并发访问的干扰,分别是锁对象和条件对象
-
锁对象
-
package czbk; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @author Xxx * @Description: 加入了线程同步(synchronized)的售票机制 * @date 2018/11/18下午4:51 **/ public class Example09 { public static void main(String[] args) { TicketWindow2 ticketWindow2 = new TicketWindow2(); new Thread(ticketWindow2, "窗口1").start(); new Thread(ticketWindow2, "窗口2").start(); new Thread(ticketWindow2, "窗口3").start(); new Thread(ticketWindow2, "窗口4").start(); TicketWindow3 ticketWindow3 = new TicketWindow3(); new Thread(ticketWindow3, "窗口1").start(); new Thread(ticketWindow3, "窗口2").start(); new Thread(ticketWindow3, "窗口3").start(); new Thread(ticketWindow3, "窗口4").start(); } } class TicketWindow2 implements Runnable { private Object lock = new Object(); // 这个变量是ticketWindow2的,被所有的线程共享 private int tickets = 100; // 每个线程被处理器调度时都会执行该方法 @Override public void run() { while (true) { // 在synchronized方法块中的代码,一个时刻只允许一个线程访问. // lock的初始值为1,当有线层访问该方法时,会将lock的值置为0,禁止其他线程访问,访问完毕后将lick的值置为1,允许其他线程访问. synchronized (lock) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } if (tickets >= 1) System.out.println(Thread.currentThread().getName() + "正在发售第:" + tickets-- + "张票"); else break; } } } } class TicketWindow3 implements Runnable { private Lock lock = new ReentrantLock(); private int tickets2 = 100000; @Override public void run() { while (true) { lock.lock(); try { if (tickets2 >= 1) System.out.println(Thread.currentThread().getName() + "正在发售第:" + tickets2-- + "张票"); else break; } finally { lock.unlock(); } } } }
-
-
条件对象
多线程通信
-
package czbk; /** * @author Xxx * @Description: 多线程通信(条件对象加锁) * @date 2018/11/18下午5:09 **/ public class Example10 { public static void main(String[] args) { Stroge stroge = new Stroge(); Input input = new Input(stroge); Output output = new Output(stroge); new Thread(input).start(); new Thread(output).start(); } } class Input implements Runnable { private Stroge stroge; private int num; public Input(Stroge stroge) { this.stroge = stroge; } @Override public void run() { while (true) { stroge.put(num++); } } } class Output implements Runnable { private Stroge stroge; public Output(Stroge stroge) { this.stroge = stroge; } @Override public void run() { while (true) { stroge.get(); } } } class Stroge { // 存放数据的数组 private int[] cells = new int[10]; // 放入和取出的下标 private int inPos, outPos; // 数组中元素的个数 private int count; // 定义线程同步方法 public synchronized void put(int num) { try { // 当数组中元素已满时,让线程进入等待状态(加锁) while (count == cells.length) { this.wait(); } cells[inPos] = num; System.out.println("在cells[" + inPos + "]中放入数据---" + cells[inPos]); inPos++; if (inPos == cells.length) inPos = 0; count++; // 唤醒此同步锁上等待的第一个线程 this.notify(); } catch (Exception e) { e.printStackTrace(); } } public synchronized void get() { try { while (count == 0) this.wait(); int data = cells[outPos]; System.out.println("在cells[" + outPos + "]中取出数据---" + cells[outPos]); cells[outPos]=0; outPos++; if (outPos == cells.length) outPos = 0; count--; this.notify(); } catch (Exception e) { e.printStackTrace(); } } }