Java并发编程--1.Thread和Runnable
创建线程
Java有两种方式创建线程, 继承Thread类和实现Runnable接口
继承Thread
步骤:
1.自定义一个类继承Thread类, 重写run方法
2.创建自定义类的对象,调用start()
例如:
class MyThread extends Thread { @Override public void run() { System.out.println("in thread"); } } MyThread thread = new MyThread(); thread.start();
实现Runnable
步骤:
1. 自定义一个类,实现Runnable,重写run()
2.创建一个Thread对象, 构造方法的参数是自定义类的对象, 调用start()
例如:
class MyRunnable implements Runnable { @Override public void run() { System.out.println("in Runable"); } } MyRunnable runnable = new MyRunnable(); new Thread(runnable).start();
Thread和Runnable的区别
买火车票的案例
买5张火车票,我们希望多个线程总共买5张票, 下面是两种实现的代码
继承Thread:
class MyThread extends Thread { private int ticket = 5; @Override public void run() { for (int i=0;i<10;i++) { if(ticket > 0){ System.out.println("ticket = " + ticket--); } } } } new MyThread().start(); new MyThread().start();
我们new了2个线程对象,分别独立的执行2个对象中的代码
控制台输出: 忽略输出顺序,可以看出2个线程分别卖了5张
ticket = 5 ticket = 4 ticket = 3 ticket = 5 ticket = 2 ticket = 4 ticket = 3 ticket = 2 ticket = 1 ticket = 1
实现Runnable接口:
class MyRunnable implements Runnable { private int ticket = 5; @Override public void run() { for (int i=0;i<10;i++) { if(ticket > 0){ System.out.println("ticket = " + ticket--); } } } } MyRunnable r = new MyRunnable(); new Thread(r).start(); new Thread(r).start();
两个Thread对象共享一个Runnable对象
控制台输出: 可以看出2个线程共买了5张, 达到了资源共享的目的
ticket = 5 ticket = 4 ticket = 3 ticket = 2 ticket = 1
Runnable的优势
通过上面的案例, 可以总结出:
1.数据能够被多个线程共享,实现了代码与数据是独立的
2.适合多个相同程序代码的线程区处理同一资源的情况
改变线程状态
线程中断: interrupt
线程的中断是一种协作机制,线程中断后并不一定立即中断,而是要求线程在合适的时间中断自己,每个线程都有一个boolean的中断标志,该属性不再Thread中
interrupt() : 只是设置线程的中断标志
public static void main(String[] args) { Runnable r = new Runnable() { @Override public void run() { try { Thread.sleep(5000); } catch (InterruptedException e) { //如果去掉这句, 下面的输出语句就会答应 return; } System.out.println("中断"); } }; Thread t = new Thread(r); t.start(); //主线程休眠,确保刚才启动的线程执行一段时间 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } //中断线程 t.interrupt();
在主线程中启动新线程, 主线程休眠2秒钟, 新线程休眠5秒钟
2秒后主线程会中断新线程,新线程的休眠状态被打断, 抛出 InterruptedException
程序进入catch块中,执行return语句, 从run()返回,然后线程消亡
interrupted() : 线程中断返回true, 并清除线程状态
isInterrupted(): 线程中断返回true, 不能改变线程的状态
public static void main(String[] args) { System.out.println(Thread.interrupted()); //中断 Thread.currentThread().interrupt(); System.out.println(Thread.interrupted()); System.out.println(Thread.interrupted()); } //控制台输出: false true false
也就是说, interrupted()会改变中断状态
线程挂起 : Joining
join() : 在A线程中,调用B线程对象的该方法, 那么A线程等B线程执行完后再执行
public static int a = 0; public static void main(String[] args) throws Exception { Runnable r = new Runnable(){ @Override public void run() { for (int k = 0; k < 5; k++) { a = a + 1; } System.out.println("a" + a); } }; Thread t = new Thread(r); t.start(); t.join(); for (int i=0; i<1; i++) { System.out.println("i=" + i); } }
控制台输出: a=5 i=0;
如果把 t.join()该行去掉, 则输出 i=0 a=5 ; 因为主线程首先获得时间片执行, 然后在执行其它线程
线程间通信: wait-notify 实现生产者 - 消费者模型
通过Object类的wait(), notify(), notifyAll()可以实现线程间的通信
wait() : 将当前线程置入休眠状态,直到接到通知或被中断为止
notify() : 如果有多个线程等待,则线程规划器任意挑选出其中一个wait()状态的线程来发出通知
nofityAll() : 使所有原来在该对象上wait的线程统统退出wait的状态
生产者和消费者在同一时间段共享同一存储空间, 生产者向空间内生产数据,消费者取出数据
下面是个例子:
public class ProductConsumer{ public static void main(String[] args) { Shared s = new Shared(); new Producer(s).start(); new Consumer(s).start(); } } /** 负责存储数据 */ class Shared { private char c; private volatile boolean writeable = true; synchronized void setSharedChar(char c) { while (!writeable) try { wait(); } catch (InterruptedException ie){ } this.c = c; System.out.println(c + " produced by producer."); writeable = false; notify(); } synchronized char getSharedChar(){ while (writeable) try{ wait(); } catch (InterruptedException ie){ } writeable = true; notify(); System.out.println(c + " consumed by consumer."); return c; } } /** 生产者 */ class Producer extends Thread{ private final Shared s; Producer(Shared s){ this.s = s; } @Override public void run() { for (char ch = 'A'; ch <= 'Z'; ch++){ synchronized (s) { s.setSharedChar(ch); } } } } /** 消费者 */ class Consumer extends Thread { private final Shared s; Consumer(Shared s) { this.s = s; } @Override public void run() { char ch; do { synchronized (s) { ch = s.getSharedChar(); } } while (ch != 'Z'); } }
控制台输出:
A produced by producer.
A consumed by consumer.
...................
线程状态总结
现在我们能创建线程, 并能改变线程的状态,下图是对线程状态的总结