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.

...................

线程状态总结

现在我们能创建线程, 并能改变线程的状态,下图是对线程状态的总结

posted @ 2017-04-05 17:32  liuconglin  阅读(343)  评论(0编辑  收藏  举报