JAVA并发编程

JAVA并发编程

JAVA创建线程的3种方式

继承Thread

class MyThread extends Thread {
    private int i = 0;
    @Override
    public void run() {
        for (i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }
}

Runnable

没有返回值

class MyRunnable implements Runnable {
    private int i = 0;
    @Override
    public void run() {
        for (i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }
}

Callable

有返回值

public class ThreadTest {
    public static void main(String[] args) {
        Callable<Integer> myCallable = new MyCallable();    // 创建MyCallable对象
        FutureTask<Integer> ft = new FutureTask<Integer>(myCallable); //使用FutureTask来包装MyCallable对象
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
            if (i == 30) {
                Thread thread = new Thread(ft);   //FutureTask对象作为Thread对象的target创建新的线程
                thread.start();                      //线程进入到就绪状态
            }
        }

        System.out.println("主线程for循环执行完毕..");
        
        try {
            int sum = ft.get();            //取得新创建的新线程中的call()方法返回的结果
            System.out.println("sum = " + sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}


class MyCallable implements Callable<Integer> {
    private int i = 0;

    // 与run()方法不同的是,call()方法具有返回值
    @Override
    public Integer call() {
        int sum = 0;
        for (; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
            sum += i;
        }
        return sum;
    }
}

进程和线程

  • 进程: 资源分配的最小单位,具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。
  • 线程:操作系统进行运算调度的最小单位,被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈,自己的寄存器环境,自己的线程本地存储

一个进程可以包含多个线程,至少一个。

java默认有两个线程,一个main,一个垃圾回收。

java无法直接操作硬件,要通过C++的native方法来开启线程

并发和并行

  • 并发:把任务在不同的时间点交给处理器进行处理。在同一时间点,任务并不会同时运行。
  • 并行:把每一个任务分配给每一个处理器独立完成。在同一时间点,任务一定是同时运行。

并发编程是为了充分利用CPU的资源

线程的状态

线程的六种状态

在这里插入图片描述

public enum State {
    // 新建
    NEW,

    // 运行
    RUNNABLE,

    // 阻塞
    BLOCKED,

    // 等待,一直等待
    WAITING,
	
    // 超时等待
    TIMED_WAITING,

    // 终止
    TERMINATED;
}

wait和sleep

  • wait来自于object类,会释放锁,不需要捕获异常,一般在同步块中使用

  • sleep来自于thread类,不会释放锁,需要捕获异常

sleep的方式

TimeUnit.DAYS.sleep(1);
TimeUnit.SECONDS.sleep(10);

Lock

锁分为公平锁和非公平锁

公平:每个线程获取锁的机会是平等的

非公平:每个线程获取锁的机会是不平等的

无锁情况下

不加锁时,最终剩余的结果和期望的结果有偏差

public class Ticket {
    private int num = 50;

    public void sale(){
        if(num > 0){
            System.out.println(Thread.currentThread().getName() + "剩余" + --num + "张票" );
        }
    }
}
public class Main {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(() ->{
            sale10(ticket);
        }, "A").start();
        new Thread(() ->{
            sale10(ticket);
        }, "B").start();

        new Thread(() ->{
            sale10(ticket);
        }, "C").start();
    }

    private static void sale10(Ticket ticket){
        for (int i = 0; i < 10; i++) {
            ticket.sale();
        }
    }
}
...
A卖票后剩余11张票
A卖票后剩余10张票
...
C卖票后剩余2张票
C卖票后剩余1张票
A卖票后剩余9张票

synchronized

本质是加了个队列,是非公平锁。

public class Ticket {
    private int num = 30;

    public synchronized void sale(){
        if(num > 0){
            System.out.println(Thread.currentThread().getName() + "卖票后剩余" + --num + "张票" );
        }
    }
}
A卖票后剩余29张票
A卖票后剩余28张票
B卖票后剩余27张票
...
C卖票后剩余1张票
C卖票后剩余0张票

ReentrantLock

ReentrantLock参数为boolean,true表示是公平锁,默认为非公平锁。如果使用公平锁,从打印的日志可以看出,ABC基本是交替出现的。这样调度效率比较低。

import java.util.concurrent.locks.ReentrantLock;

public class Ticket {
    ReentrantLock lock = new ReentrantLock();
    private int num = 30;

    public void sale(){
        lock.lock();
        if(num > 0){
            System.out.println(Thread.currentThread().getName() + "卖票后剩余" + --num + "张票" );
        }
        lock.unlock();
    }
}
A卖票后剩余29张票
A卖票后剩余28张票
...
A卖票后剩余1张票
A卖票后剩余0张票

synchronized和lock的区别

  • synchronized是java内置的关键字;lock为一个java类
  • synchronized无法获取锁的状态;lock可以用以判断是否获取了锁
  • synchronized会自动释放锁;lock必须要手动释放锁,如果不是放锁会思索
  • synchronized可以重入,不可以中断,是非公平的;lock也是可重入锁,可以判断锁状态,可以是公平也可以是非公平。
  • synchronized适合锁少量代码的同步(代码块、方法、类名);lock适合锁大量代码

虚假唤醒

使用if判断,唤醒后线程会从wait之后的代码开始运行,但是不会重新判断if条件,直接继续运行if代码块之后的代码,而如果使用while的话,也会从wait之后的代码运行,但是唤醒后会重新判断循环条件,如果不成立再执行while代码块之后的代码块,成立的话继续wait。

public class LockTest {
    public static void main(String[] args) {
        Clerk clerk = new Clerk();
        Producter producter = new Producter(clerk);
        Customer customer = new Customer(clerk);

        new Thread(producter,"生产者A").start();
        new Thread(customer,"消费者A").start();
        new Thread(producter,"生产者B").start();
        new Thread(customer,"消费者B").start();
    }
}
// 售货员
class Clerk {
    private int product = 0;

    // 进货
    public synchronized void add() {
        // 产品已满
        if (product >=1) {
            System.out.println(Thread.currentThread().getName() + ": " + "已满!");
            try {
                this.wait();
            } catch (InterruptedException e) {
            }
        }
        ++product;
        // 该线程从while中出来的时候,是满足条件的
        System.out.println(Thread.currentThread().getName() + ": " +"....................进货成功,剩下"+product);
        this.notifyAll();
    }

    // 卖货
    public synchronized void sale() {
        if (product <=0) {
            System.out.println(Thread.currentThread().getName() + ": " + "没有买到货");
            try {
                this.wait();
            } catch (InterruptedException e) {
            }
        }
        --product;
        System.out.println(Thread.currentThread().getName() + ":买到了货物,剩下 " + product);
        this.notifyAll();
    }
}
// 生产者
class Producter implements Runnable {
    private Clerk clerk;

    public Producter(Clerk clerk) {
        this.clerk = clerk;
    }

    // 进货
    @Override
    public void run() {
        for(int i = 0; i < 20; ++i) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
            }
            clerk.add();
        }
    }
}

// 消费者
class Customer implements Runnable {
    private Clerk clerk;
    public Customer(Clerk clerk) {
        this.clerk = clerk;
    }

    // 买货
    @Override
    public void run() {
        for(int i = 0; i < 20; ++i) {
            clerk.sale();
        }
    }
}

JUC生产者消费者

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Clerk {
    private int product = 0;

    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    // 进货
    public void add() {
        lock.lock();
        try {
            while (product >= 1){
                System.out.println(Thread.currentThread().getName() + ": " + "已满!");
                condition.await();
            }
            System.out.println(Thread.currentThread().getName() + ": " +"....................进货成功,剩下"+product);
            ++product;
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    // 卖货
    public synchronized void sale() {
        lock.lock();
        try {
            while (product >= 1){
                System.out.println(Thread.currentThread().getName() + ": " + "已空!");
                condition.await();
            }
            System.out.println(Thread.currentThread().getName() + ":买到了货物,剩下 " + product);
            --product;
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        this.notifyAll();
    }
}
condition

condition可以精准的通知和唤醒线程,按照A->B->C的方式执行

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Data {
    private int num = 1;

    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();


    public void print1() {
        lock.lock();
        try {
            while (num != 1) {
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName() + "正在执行");
            num = 2;
            condition2.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void print2() {
        lock.lock();
        try {
            while (num != 2) {
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName() + "正在执行");
            num = 3;
            condition3.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void print3() {
        lock.lock();
        try {
            while (num != 3) {
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName() + "正在执行");
            num = 1;
            condition1.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
public static void main(String[] args) {
    Data data = new Data();
    new Thread(() -> {
        for (int i = 0; i < 10; i++) {
            data.print1();
        }
    }, "A").start();

    new Thread(() -> {
        for (int i = 0; i < 10; i++) {
            data.print2();
        }
    }, "B").start();

    new Thread(() -> {
        for (int i = 0; i < 10; i++) {
            data.print3();
        }
    }, "C").start();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值