juc是java util concurrent的简写,即java原生的并发包。
要明白java并发包,必须要先理解并发和并行的区别
并发和并行的区别
**并发:**指的是多个线程对同一个资源交替执行的过程。
**并行:**指的是多个线程同时执行。
线程的6种状态
java源码可知,线程有6种状态即NEW(新建) RUNNABLE( 运行) BLOCKED (阻塞) WATTING(等待)延迟等待(TIMED_WATTING)终止(TERMINATED)
wait/sleep的区别
-
类不同
wait:Obejct类的方法
sleep是thread类的方法
线程中的休眠一般不使用上述方法 一般适用TimeUnit.SECONDS.sleep(3). (休眠三秒) -
会不会释放资源
sleep抱着锁睡觉,不释放
wait会释放 -
适用范围不同
wait和notify是一组,一般线程通讯使用
sleep是一个单独的方法,在哪里都可以用 -
关于异常
sleep需要捕获异常
关键点:lock锁
synchronized锁是非LOCK锁,是比较传统的方式,然而不适合常用。
synchronize锁和lock锁的区别:lock锁是一个对象,synchronized是一个关键字,synchronized会自动释放锁,lock锁需要手动释放锁。从而会造成,如果A线程死锁,使用synchronized的线程B会一直等待下去,但是Lock可以通过尝试获取锁,失败就会放弃。
在代码量比较大的时候,一般适用Lock精准控制锁。
synchronized版
写并行线程的思路一句话总结:线程操作资源类(符合设计模式不耦合的原则)
public class Demo01 {
public static void main(String[] args) throws InterruptedException {
// 1、新建资源类
Ticket ticket = new Ticket();
// 2、线程操纵资源类
new Thread(new Runnable() {
public void run() {
for (int i = 1; i <=40; i++) {
ticket.saleTicket();
}
}
},"A").start();
new Thread(new Runnable() {
public void run() {
for (int i = 1; i <=40; i++) {
ticket.saleTicket();
}
}
},"B").start();
new Thread(new Runnable() {
public void run() {
for (int i = 1; i <=40; i++) {
ticket.saleTicket();
}
}
},"C").start();
}
}
// 单独的资源类,属性和方法!
// 这样才能实现复用!
class Ticket{
private int number = 30;
// 同步锁,厕所 =>close=>
// synchronized 这是一个关键字
public synchronized void saleTicket(){
if (number>0){
System.out.println(Thread.currentThread().getName() + "卖出第"+(number--)+"票,还剩:"+number);
}
}
}
如果是LOCK锁 只需要对资源类进行略改动
class Ticket{
private Lock lock. =new ReentrantLock()
private int number = 30;
// 同步锁,厕所 =>close=>
// synchronized 这是一个关键字
public void saleTicket(){
lock.lock();//加锁
try{
if (number>0){
System.out.println(Thread.currentThread().getName() + “卖出第”+(number–)+“票,还剩:”+number);
}
}catch(Exception){
e.printStackTrace();
}finally{
lock.unlock();//手动解锁
}
}
}
如何用synchronized写消费者生产者模式呢?还是三步走线程操纵资源类,不过在写资源类的时候需要有唤醒其他线程的操作,会用到wait notify成对的操作。(资源类的写法也是三步骤1.判断,执行,通知,即判断是否需要休眠,如果不需要就执行,执行完通知其他线程)
如synchronized版
package com.coding.demo01;
// Synchronized 版
/*
目的: 有两个线程:A B ,还有一个值初始为0,
实现两个线程交替执行,对该变量 + 1,-1;交替10次
*/
public class Demo03 {
public static void main(String[] args) {
Data data = new Data();
// +1
new Thread(()->{
for (int i = 1; i <=10 ; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
// -1
new Thread(()->{
for (int i = 1; i <=10 ; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
// 资源类
// 线程之间的通信: 判断 执行 通知
class Data{
private int number = 0;
// +1
public synchronized void increment() throws InterruptedException {
while (number!=0){ // 判断是否需要等待
this.wait();
}
number++; // 执行
System.out.println(Thread.currentThread().getName()+"\t"+number);
// 通知
this.notifyAll(); //唤醒所有线程
}
// -1
public synchronized void decrement() throws InterruptedException {
while(number==0){ // 判断是否需要等待
this.wait();
}
number--; // 执行
System.out.println(Thread.currentThread().getName()+"\t"+number);
// 通知
this.notifyAll(); //唤醒所有线程
}
}
上述是两个线程的消费者和生产者模式,如果有三个或者是三个以上的线程交替进行显然就不行了,因为上面notifyall()是唤醒其他休眠的线程,没有指定唤醒某个线程,如果线程比较的话,就会乱套。如果需要精准唤醒某个线程需要使用ReentrantLock类中的Condition类的await(休眠) signal(唤醒)
public class Demo04 {
public static void main(String[] args) {
Data2 data = new Data2();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
data.print5();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
data.print10();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
data.print15();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
}
}
// 资源类
class Data2{
private int number = 1; // 1A 2B 3C
private Lock lock = new ReentrantLock();
// 实现精准访问
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
public void print5() throws InterruptedException {
lock.lock();
try {
// 判断
while (number!=1){
condition1.await();
}
// 执行
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
// 通知第二个线程干活!
number = 2;
condition2.signal(); // 唤醒
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock(); // 一定要解锁
}
}
public void print10() throws InterruptedException {
lock.lock();
try {
// 判断
while (number!=2){
condition2.await();
}
// 执行
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
// 通知3干活
number = 3;
condition3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print15() throws InterruptedException {
lock.lock();
try {
// 判断
while (number!=3){
condition3.await();
}
// 执行
for (int i = 1; i <= 15; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
// 通知 1 干活
number = 1;
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}