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) { } } } }
原文地址:https://www.cnblogs.com/ss561/p/4573313.html