zoukankan      html  css  js  c++  java
  • java多线程

    多线程是指从软件或者硬件上实现多个线程并发执行的技术,可以使计算机能够同时执行多个线程。但是需要了解的是,多线程的实现其实是处理器在多个线程之间进行着快速切换,宏观上表现出多个线程同时在执行。

    一.线程的创建

    线程的创建有两种方式,一是继承自Thread类,二是实现Runnable接口。

    1.创建类继承自线程类Thread

    步骤1:定义类继承自Thread,复写Thread类中的run()方法。该方法用来封装自定义代码,让线程运行。

    步骤2:创建子类对象,同时线程也会被创建。

    步骤3:调用线程的start()方法启动线程。该方法会自动调用run()方法。

    代码示例:

    class Model{
        public static void main(String[]args){
            Test test=new Test();//创建线程对象
            test.start();//启动线程,会自动执行run()方法,此处如果写run()方法不写start()方法,线程无法执行。
        }
    }
    class Test extends Thread{//定义类继承自Thread线程类
        public void run(){//复写run()方法
            for(int x=0; x<10; x++)
                System.out.println(x);
        }
    }

    2.实现Runnable接口来创建线程

    步骤1:定义类实现Runnable接口,并复写其中的run()方法

    步骤2:通过Thread类创建线程,并将实现了Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。

    步骤3:Thread类的对象调用start()方法来启动线程

    例如代码:

    class Model{
        public static void main(String[]args){
            Test test=new Test();
            Thread t=new Thread(test);
            t.start();
        }
    }
    class Test implements Runnable{
        public void run(){
            for(int x=0;x<10;x++){
                System.out.println(x);
            }
        }
    }

    实现方式与继承方式的区别,主要就是java不支持多继承但是支持多实现,当类继承Thread类后就不能在继承其他类了。另外两者线程代码存在的位置也不一样了,继承线程代码存放在thread子类run方法中,实现线程代码存放在runnable接口子类中。因为线程对象创建方式不同前者线程对象与操作对象一致,后者操作对象与控制对象分离,前者无法实现多线程共享数据的的操作,后者可以。

    二.线程状态

    线程具有以下几种状态 1被创建,2运行,3冻结,4消亡结束 5,临时阻塞状态。
    创建线程是通过线程类对象的创建完成的,即是通过构造器完成
    创建线程到运行时通过start()方法完成的
    由运行到冻结可以通过sleep(time)方法,这种方法time时间到就会重回运行状态,
    还有一种方法是wait()方法,这种方法进入冻结状态不会自发的回复运行,需要借助notify()唤醒方法。
    由运行到消亡,可以通过stop()方法,或者run()运行结束也会编程消亡状态。
    第五种状态比较特殊,是因为cpu的执行切换导致多个线程处于等待获取cpu执行权的状态,此时线程具有运行资格但是不具有执行权。
    进入冻结状态,是放弃了执行资格,当从冻结状态唤醒的时候是要先获取执行资格即进入临时阻塞状态,然后等待cpu执行权,所以运行状态是既具有执行资格又具有执行权。

    另外线程处于冻结状态时可能会出现线程中断异常,需要进行try catch处理。

    三.多线程的安全问题解决方式:同步

    多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致数据共享出错。这就是线程冲突导致的线程安全问题。需要注意的是线程安全的出现是因为,线程执行的延迟性与随机性,在理想情况下不一定会出现,但是一旦出现可能会造成很严重的后果。

    解决办法是让一个线程执行完毕时,其他线程才能参与进来,java对解决线程安全提供了专业的解决方式,这就是线程同步。

    1.同步代码块

    格式:

    synchronized(对象)
    {
    需要同步的代码;
    }
    同步可以解决安全问题的根本原因就在那个对象上。
    该对象如同锁的功能。

    2.同步函数

    格式:在函数上加上synchronized修饰符即可。

    同步函数的写法要比同步代码块更合理一些,不需要外建对象作为同步锁,直接使用this作为同步锁。

    另外对于静态函数的同步,使用的锁是该方法所在类的字节码文件对象。 类名.class

    3.同步需要注意的问题:死锁

    当多个同步出现嵌套的情况时,如果同步锁不同就可能会导致死锁的情况,此时线程的执行无法继续。

    4.同步的前提

    同步的前提:
    (1)同步需要两个或者两个以上的线程。
    (2)多个线程使用的是同一个锁。
    未满足这两个条件,不能称其为同步。

    5.同步的弊端:
    当线程相当多时,因为每个线程都会去判断
    同步上的锁,这是很耗费资源的,无形
    中会降低程序的运行效率。

    四.线程间通信

    线程间的通讯,主要是通过锁来实现的
    多个线程操作同一个资源,但是操作的动作不同。
    通过同步解决线程安全问题,锁就用多线程之间的共享资源,唯一的锁控制多个线程协同运行。
    线程的等待唤醒机制
    wait(),notify(),notifyAll()方法用于冻结和唤醒线程。flag布尔型变量用于决定线程是执行还是冻结,控制线程的交替进行。都是用在同步中,因为要对持有监视器锁的线程操作。
    这些方法都是定义在Object类中的,因为这些方法在操作同步中线程时,都必须要标识他们所操作线程持有的锁,只有同一个锁上的被等待线程,才可以被同一个锁上的notify唤醒。

    五.JDK生产消费问题的升级解决方案

    JDK1.5 中提供了多线程升级解决方案。
    将同步Synchronized替换成现实Lock操作。
    将Object中的wait,notify notifyAll,替换了Condition对象。
    该对象可以Lock锁 进行获取。
    该示例中,实现了本方只唤醒对方操作。

    import java.util.concurrent.locks.*;//导入包
    
    class ProducerConsumerDemo2 
    {
        public static void main(String[] args) 
        {
            Resource r = new Resource();//创建共享资源
    
            Producer pro = new Producer(r);//分别创建生产与消费的实例对象将共享数据对象作为参数进行传递。
            Consumer con = new Consumer(r);
    
            Thread t1 = new Thread(pro);//创建线程对象
            Thread t2 = new Thread(pro);
            Thread t3 = new Thread(con);
            Thread t4 = new Thread(con);
    
            t1.start();//启动线程
            t2.start();
            t3.start();
            t4.start();
    
        }
    }
    class Resource
    {
        private String name;
        private int count = 1;
        private boolean flag = false;//定义标记进行判断决定线程的状态
        private Lock lock = new ReentrantLock();//获得Lock接口实例对象
        private Condition condition_pro = lock.newCondition();//获取lock锁的监听对象
        private Condition condition_con = lock.newCondition();
    
    
    
        public  void set(String name)throws InterruptedException
        {
            lock.lock();//关闭锁
            try
            {
                while(flag)
                    condition_pro.await();//该生产监听器上的线程进入等待状态
                this.name = name+"--"+count++;
    
                System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
                flag = true;
                condition_con.signal();//唤醒消费监听器上所监视的线程对象
            }
            finally
            {
                lock.unlock();//释放锁的动作一定要执行。
            }
        }
            public  void out()throws InterruptedException
        {
            lock.lock();
            try
            {
                while(!flag)
                    condition_con.await();
                                System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);
                flag = false;
                condition_pro.signal();
            }
            finally
            {
                lock.unlock();
            }
            
        }
    }     
    class Producer implements Runnable { private Resource res; Producer(Resource res) { this.res = res; } public void run() { while(true) { try { res.set("+商品+"); } catch (InterruptedException e) { } } } } class Consumer implements Runnable { private Resource res; Consumer(Resource res) { this.res = res; } public void run() { while(true) { try { res.out(); } catch (InterruptedException e) { } } } }
  • 相关阅读:
    MS面试归来:)
    准备出发到成都
    线程的同步机制(zz)
    Windows Vista(zz)
    这几天上海热晕了
    微软官方:Vista硬件要求指南(zz)
    有趣的递归缩写(Recursive acronym)
    《三国志》生僻人名读法集(zz)
    Git bash常用命令
    各大输入法分类词库内部格式的简单比较
  • 原文地址:https://www.cnblogs.com/ss561/p/4573313.html
Copyright © 2011-2022 走看看