Java多线程学习笔记

1. 线程与多线程的概念

线程(thread),又称轻量级进程(LightWeight Process,LWP),是进程的一个实体,是被系统独立调度和分派的基本单位(进程作为系统分配资源的基本单位)。多个线程可以共享数据空间和程序代码,每个线程有自己的执行堆栈和程序计数器为其执行上下文;多线程是为了节约CPU时间。

实现多线程的方式:多个硬件处理器同时支持多线程、单个硬件处理器以时间分片方式支持多线程、多个硬件处理器以时间分片方式支持多线程。

多线程的优点:提高交互式应用程序的响应性、线程的上下文切换较之进程的开销低、实现计算机任务之间的资源共享。

多线程的缺点:设计复杂存在同步、死锁、公平等问题;多线程应用程序的行为通常与特定的平台有关,可移植性降低。

2. 线程的状态及生命周期

java线程的状态切换详图
这里写图片描述

线程生命周期的五个状态:New(新建)、Runnable(就绪)、Running(运行)、Dead(终止、死亡)、Blocked(堵塞)

线程的创建方式有两种:实现Runnable接口或者继承Thread类 并实现其中的run()方法。

3. 线程中常用方法详解

  • setPriority(intnew Priority)设置线程优先级(1-10),基数越大优先级越高、越先执行。JVM的线程调度机制会根据优先级大小决定哪个线程先执行,优先级高的线程会抢占低优先级线程的CPU时间片。注意:调度机制不会保证优先级高的线程就一定先执行,故不要将算法语义的正确性建立在线程优先级的基础上。
  • join( ) 该方法做线程的串行处理。 如果一个线程执行过程中要用到另一个线程的运行结果、则可进行线程的串行处理。(调用该方法的线程将立即进入阻塞状态,直到加入的线程执行结束,当前线程才能进入到就绪状态,等待被调度执行)
  • yield( ) 线程让步。 让运行中的线程主动放弃当前获得CPU的处理机会,从而转入就绪状态;让其他与该线程具有相同优先级的线程有机会运行。(主要用在:占用数据库连接资源的线程、占用计算机端口资源的线程。 尽快释放这些紧缺资源,以供其他任务使用)
  • wait( )/notify( ) notifyAll( ) 这些方法只能用在synchronized机制中,否则会抛出异常。 这三个方法都是Object类中的方法,能在所有类的对象中使用。
  • sleep( ) 使当前线程睡眠指定的时间,可以用interrupt( )方法终止。

4. 守护线程和同步、锁问题

守护线程也称后台线程,为其他线程提供服务。守护线程不作为应用线程的核心,所有非守护线程(用户线程)终止运行后,守护线程还可以继续运行一段时间。
isDaemon()方法判断一个线程是否为守护线程。
setDaemon(boolean) 方法讲一个线程设置为守护线程。

synchronized 能给方法和代码块加锁,给方法加锁只能锁定调用该方法的对象、给代码块加锁可以锁定任意指定的对象如:synchronized( 对象名){ 代码块 }。
注意:

  • synchronized不能同步类和变量
  • 线程睡眠时,它产生的任何锁都不会被释放
  • synchronized同步损害并发性,应该尽可能缩小同步范围
  • 注意静态方法的同步锁,锁定的是类对象

5. 经典问题解析(生产者、消费者问题)

package oracle.hl.threads.consumerandproducer2;
/**
 * 
 * 生产者和消费者共享的资源栈 
 * 1、问题在于多个线程可以同时地访问共享资源,
 * 并且某一时刻只允许有单个线程更新资源。
 * @author admin
 *
 */
public class SynStack {
    private int index = 0;
    private char [] data = new char[3];
    private boolean available = false;

    public synchronized void push(char c){
        //当栈中资源已满,停止生产线程。
        // 控制在同一时刻只能有单一线程向共享栈中写入数据;
        while(index==data.length||available){       
            try {
                this.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        //如果栈中资源未满,向栈中添加资源,同时唤醒消费线程
        available = true;   //设置为true表示有线程正在访问共享资源
        this.notifyAll();
        data[index] = c;
        index++;
        System.out.println("Produced:"+c);
    }

    public synchronized char pop(){
        //当栈中没有资源可以消费,则停止消费者线程。
        //控制每一时刻只能有一个消费者对栈中资源消费(即:更改资源)
        while(index == 0||!available){
            try {
                this.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        //当共享栈中存在可消费资源,减少栈中资源,同时唤醒生产者线程。
        available = false;
        this.notifyAll();
        index--;
        System.out.println("consumer:"+data[index]);
        return data[index];
    }
}

//生产者
package oracle.hl.threads.consumerandproducer2;

public class Producer implements Runnable{
    private SynStack stack;
    private String producer;

    public Producer(SynStack stack,String producer){
        this.stack = stack;
        this.producer = producer;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            char c = (char)(Math.random()*26+'A');
            stack.push(c);
            System.out.println(producer+"  生产--->"+c);
            try {
            //休眠指定时间,测试时能够看清生产者消费者的距离逻辑
                Thread.currentThread().sleep((int)(Math.random()*100));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    } 

}

package oracle.hl.threads.consumerandproducer2;
//消费者
public class Consumer implements Runnable{
    private SynStack stack;
    private String consumer;

    public Consumer(SynStack stack,String consumer){
        this.stack = stack;
        this.consumer = consumer;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {

            char c = stack.pop();
            System.out.println(consumer+"  消费----> "+c);
            try {
            //设置休眠时间,能够清晰看出生产者消费者的具体逻辑
                Thread.currentThread().sleep((int)(Math.random()*10000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    } 
}

package oracle.hl.threads.consumerandproducer2;
//主测试函数
public class ProducerCousumerProblem {
    public static  void main(String args[]){
        SynStack stack = new SynStack();

        Runnable p =  new Producer(stack,"p1");
        Runnable c = new Consumer(stack,"c1");
        Runnable c2 = new Consumer(stack,"c2");

        Thread pThread = new Thread(p);
        Thread cThread = new Thread(c);
        Thread c2Thread = new Thread(c2);

        pThread.start();
        cThread.start();
        c2Thread.start();
    }
}

6. 总结

使用线程解决问题时主要得考虑:线程同步问题(死锁、互斥),要防止程序出现死锁现象。要准确地使用synchronized解决同步问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值