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();
}