欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

java核心技术-多线程基础

程序员文章站 2022-08-05 20:24:15
进程、线程 ​ 进程(Process) 是程序的运行实例。例如,一个运行的 Eclipse 就是一个进程。进程是程序向操作系统申请资源(如内存空间和文件句柄)的基本单位。线程(Thread)是进程中可独立执行的最小单位。一个进程可以包含多个线程。进程和线程的关系,好比一个营业中的饭店与其正在工作的员 ......

进程、线程

​ 进程(process) 是程序的运行实例。例如,一个运行的 eclipse 就是一个进程。进程是程序向操作系统申请资源(如内存空间和文件句柄)的基本单位。线程(thread)是进程中可独立执行的最小单位。一个进程可以包含多个线程。进程和线程的关系,好比一个营业中的饭店与其正在工作的员工之间的关系。

1.1 线程的创建、启动与运行

在 java 中实现多线程主要用两种手段,一种是继承 thread 类,另一种就是实现 runnable 接口。(当然还有callable和线程池)。下面我们就分别来介绍这两种方式的使用,其他请关注此博客下文。

(1).继承thread的类

public class primethread extends thread{
    //线程执行体
    @override
    public void run(){
        
        for(int i = 0; i < 100; i++){
            if(i % 2 == 0){
                system.out.println(thread.currentthread().getname() + "=" + i);
            }
        }
        
    }
    
}
public class testthread {
    public static void main(string[] args) {
        //新建一个线程
        primethread p1 = new primethread();
        //启动一个线程
        p1.start();
        
        primethread p2 = new primethread();
        p2.start();
        
        
        for(int i = 0; i < 100; i++ ){
            if(i % 2 == 0){
                system.out.println(thread.currentthread().getname() + "=" + i);
            }
        }
        
    }
}

(2).实现 runnable接口

public class ticket implements runnable{
    
    private int ticket = 100;

    @override
    public void run() {
        while(ticket > 0){
            system.out.println(thread.currentthread().getname() + "=" + --ticket);
        }
        
    }
}
public class testthread2 {
    public static void main(string[] args) {
        
        ticket ticket = new ticket();
        
        //虽然是实现了runnable接口 本质上只是实现了线程执行体 启动工作还是需要thread类来进行
        thread t1 = new thread(ticket,"售票窗口一");
        t1.start();
        
        thread t2 = new thread(ticket,"售票窗口二");
        t2.start();
        
        thread t3 = new thread(ticket,"售票窗口三");
        t3.start();
    }
}

两种实现方式的对比:

1.从面向对象编程角度看:第一种创建方式(继承thread类) 是一种基础继承的技术,第二种创建方式(以runnable接口实例为构造器参数直接通过new创建thread实例)是一种基础组合的技术。方式二不仅会避免单继承的尴尬,也会降低类与类之间的耦合性。

2.从对象共享角度看:第二种创建方式意味着多个线程实例可以共享同一个runnable实例。而第一种方式则需要依赖static关键字来完成操作。

1.2 线程的控制

java的调度方法

同优先级线程组成先进先出队列(先到先服务),使用时间片策略

对高优先级,使用优先调度的抢占式策略

thread类的相关方法:

  • sleep(long millis) : 是 thread 类中的静态方法,使当前线程进入睡眠状态
  • join() / join(long millis) : 是一个实例方法,使当前线程进入阻塞状态
  • interrupt() : 中断阻塞状态的线程
  • isalive() : 判断当前线程是否处于存活状态
  • yield() : 线程让步
public class testthread3 {
    public static void main(string[] args) throws exception {
        
        thread t1 = new thread(new runnable() {
            @override
            public void run() {
                for(int i = 1; i < 100;i++){
                    if(i % 2 == 0){
                    system.out.println(thread.currentthread().getname() + "=" + i);
                    }
                }
                    
            }
        },"线程1");
        t1.start();
        //线程1在 sleep之前就执行完了
        t1.sleep(10000);
        //join方法 迫使t2 必须等线程1 执行完 才能执行 然而 t1输出完自己的 睡着了 t2*等了10秒
        t1.join();

        thread t2 = new thread(new runnable() {
            @override
            public void run() {
                for(int i = 1; i < 100;i++){
                    if(i % 2 != 0){
                        system.out.println(thread.currentthread().getname() + "=" + i);
                    }
                }
            }
        },"线程2");
        t2.start();
        
    }
}

java核心技术-多线程基础

1.3 线程的同步

线程同步:模拟售票程序,实现三个窗口同时售票 100 张 (1.1案例)

问题:当三个窗口同时访问共享数据时,产生了无序、重复、超额售票等多线程安全问题

解决:将需要访问的共享数据“包起来”,视为一个整体,确保一次只能有一个线程执行流访问该“共享数据”

java给上述问题提供了几种相应的解决方法

(1).同步代码块

synchronized(同步监视器){

//需要访问的共享数据

}

同步监视器 : 俗称“锁”,可以使用任意对象的引用充当,注意确保多个线程持有同一把锁(同一个对象)

(2).同步方法

同步方法 : 在方法声明处加 synchronized. 注意:非静态同步方法隐式的锁 ---- this

例如:

public synchronized void show(){}

(3).同步锁

同步锁 : lock 接口

同步代码块

public class safeticket implements runnable{
    
    private int ticket = 100;
    
    @override
    public void run() {
        while(true){
            //使用同步代码块
            synchronized (this) {
                if(ticket > 0){
                        system.out.println(thread.currentthread().getname() + " 完成售票,余票:" + --ticket);
                }
            }
            
            
        }
    }

}

同步方法:

public class safeticket implements runnable{
    
    private int ticket = 100;
    
    @override
    public void run() {
        while(true){
            //使用同步代码块
            sale();
            
        }
    }
    
    public synchronized void sale(){
        if(ticket > 0){
            system.out.println(thread.currentthread().getname() + " 完成售票,余票:" + 
--ticket);
        }
    }
    
}

同步锁

public class safeticket implements runnable{
    
    private int ticket = 100;
    
    private lock l = new reentrantlock();
    
    @override
    public void run() {
        while(true){
            l.lock();
            try {
                if(ticket > 0){
                    system.out.println(thread.currentthread().getname() + " 完成售票,余票:" + 
--ticket);
                }
            } finally {
                l.unlock();//释放锁
            }
            
        }
    }
    
}

死锁

死锁 是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于 死锁 状态或系统产生了 死锁 ,这些永远在互相等待的进程称为 死锁 进程

public class testdeadlock {
    public static void main(string[] args) {
        final stringbuffer s1 = new stringbuffer();
        final stringbuffer s2 = new stringbuffer();
        
        new thread() {
            public void run() {
                synchronized (s1) {
                    s2.append("a");
                    
                    try {
                        thread.sleep(10);
                    } catch (interruptedexception e) {
                        e.printstacktrace();
                    }
                    
                    
                    synchronized (s2) {
                        s2.append("b");
                        system.out.print(s1);
                        system.out.print(s2);
                    }
                }
            }
        }.start();
        
        new thread() {
            public void run() {
                synchronized (s2) {
                    s2.append("c");
                    synchronized (s1) {
                        s1.append("d");
                        system.out.print(s2);
                        system.out.print(s1);
                    }
                }
            }
        }.start();
    }
}

1.4 线程的通信

在 java.lang.object 类中:

wait() : 使当前“同步监视器”上的线程进入等待状态。同时释放锁

notify() / notifyall() : 唤醒当前“同步监视器”上的(一个/所有)等待状态的线程

注意:上述方法必须使用在同步中

场景1:使用两个线程打印 1-100 线程1和线程2交替打印

public class mythread implements runnable{
    
    int i = 0;
    
    @override
    public void run() {
        
        while(true){
            synchronized (this) {
                this.notify();
                
                if(i <= 100){
                    system.out.println(thread.currentthread().getname() + "=" + i++);
                }
                
                try {
                    this.wait();
                } catch (interruptedexception e) {
                }
            }
            
        }
        
    }
    
}
public class testthread4 {
    public static void main(string[] args) {
        mythread mythread = new mythread();
        
        thread t1 = new thread(mythread,"线程1");
        thread t2 = new thread(mythread,"线程2");
        
        t1.start();
        t2.start();
    }
}

经典例题:生产者/消费者问题

  • 生产者(productor)将产品交给店员(clerk),而消费者(customer)从店员处取走产品,
  • 店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫生产者停一下,
  • 如果店中有空位放产品了再通知生产者继续生产;
  • 如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。
public class testproduct {
    
    public static void main(string[] args) {
        clerk clerk = new clerk();
        
        productor pro = new productor(clerk);
        customer cus = new customer(clerk);
        
        new thread(pro).start();
        new thread(cus).start();
    }

}

// 店员
class clerk {

    private int product;

    // 进货
    public synchronized void getproduct() {
        if (product >= 20) {
            system.out.println("产品已满!");
            
            try {
                wait();
            } catch (interruptedexception e) {
            }
            
        } else {
            system.out.println("生产者生产了第" + ++product + " 个产品");
            
            notifyall();
        }
    }

    // 卖货
    public synchronized void saleproduct() {
        if (product <= 0) {
            system.out.println("缺货!");
            
            try {
                wait();
            } catch (interruptedexception e) {
            }
            
        } else {
            system.out.println("消费者消费了第" + --product + " 个产品");
            
            notifyall();
        }
    }

}

// 生产者
class productor implements runnable {

    private clerk clerk;

    public productor() {
    }

    public productor(clerk clerk) {
        this.clerk = clerk;
    }

    public clerk getclerk() {
        return clerk;
    }

    public void setclerk(clerk clerk) {
        this.clerk = clerk;
    }

    @override
    public void run() {
        while (true) {
            clerk.getproduct();
        }
    }

}

// 消费者
class customer implements runnable {

    private clerk clerk;

    public customer() {
    }

    public customer(clerk clerk) {
        this.clerk = clerk;
    }

    public clerk getclerk() {
        return clerk;
    }

    public void setclerk(clerk clerk) {
        this.clerk = clerk;
    }

    @override
    public string tostring() {
        return "customer [clerk=" + clerk + "]";
    }

    @override
    public void run() {
        while(true){
            clerk.saleproduct();
        }
    }

}