zoukankan      html  css  js  c++  java
  • 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.

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

    线程状态总结

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

  • 相关阅读:
    Mysql优化(1) 存储引擎、数据类型、字符集
    Mysql常用命令(5) 增加表的字段、修改表名、备份数据库
    Mysql常用命令(4) 插入、查询、删除、修改数据
    Navicat Premium v12.1.28 中文最新破解版(附:激活工具)
    MySQL命令:创建数据库、插入数据
    mysql初始化密码错误
    mysql第一次安装成功后初始化密码操作步骤
    MYSQL安装出现问题(服务无法启动,The service already exists)
    练习4-7 求e的近似值 (15 分)
    习题3-2 高速公路超速处罚 (15 分)
  • 原文地址:https://www.cnblogs.com/liuconglin/p/6667662.html
Copyright © 2011-2022 走看看