一个进程可以包含多个线程,一个进程至少有一个线程。
并发:多个线程操作同一个资源,交替执行的过程。
并行:多个线程同时执行,只有在多核Cpu下才能完成!
1、NEW 新建
2、RUNABLE 运行
3、BLOCKED 阻塞
4、WAITING 等待
5、 TIMED_WAITING 延时等待
6、TERMINATED 终止
wait是一个Object类,sleep是一个Thread类
sleep不会释放锁,wait可以释放锁,
wait和notify是一组,一般在线程通信中使用,
sleep就是一个单独的方法,在哪里都可以使用,
sleep需要补货异常
在JUC编程中,线程休眠使用
TimeUnit.SECONDS.Sleep(3)
package com.coding.demo01;
// 传统的 Synchronized 是一个关键字
// Synchronized 方法 和 Synchronized 块
/*
* 我们的学习是基于企业级的开发进行的;
* 1、架构:高内聚,低耦合
* 2、套路:线程操作资源类,资源类是单独的
*/
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);
}
}
}
package com.coding.demo01;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
* JUC之后的操作
* Lock锁 + lambda表达式!
*/
public class Demo02 {
public static void main(String[] args) {
// 1、新建资源类
Ticket2 ticket = new Ticket2();
// 2、线程操作资源类 , 所有的函数式接口都可以用 lambda表达式简化!
// lambda表达式 (参数)->{具体的代码}
new Thread(()->{for (int i = 1; i <= 40 ; i++) ticket.saleTicket();},"A").start();
new Thread(()->{for (int i = 1; i <= 40 ; i++) ticket.saleTicket();},"B").start();
new Thread(()->{for (int i = 1; i <= 40 ; i++) ticket.saleTicket();},"C").start();
}
}
// 依旧是一个资源类
class Ticket2{
// 使用Lock,它是一个对象
// ReentrantLock 可重入锁:回家:大门 (卧室门,厕所门...)
// ReentrantLock 默认是非公平锁!
// 非公平锁: 不公平 (插队,后面的线程可以插队)
// 公平锁: 公平(只能排队,后面的线程无法插队)
private Lock lock = new ReentrantLock();
private int number = 30;
public void saleTicket(){
lock.lock(); // 加锁
try {
// 业务代码
if (number>0){
System.out.println(Thread.currentThread().getName() + "卖出第"+(number--)+"票,还剩:"+number);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock(); // 解锁
}
}
}
所以通过以上两个例子,我们可以发现Synchronized 和 Lock 区别
1、Synchronized 是一个关键字、Lock 是一个对象
2、Synchronized 无法尝试获取锁,Lock 可以尝试获取锁,判断
3、Synchronized 会自动释放锁(a线程执行完毕,b如果异常了,也会释放锁),lock锁是手动释放锁!如果你不释放就会死锁。
4、Synchronized (线程A(获得锁,如果阻塞),线程B(等待,一直等待);)lock,可以尝试获取锁,失败了之后就放弃
5、Synchronized 一定是非公平的,但是 Lock 锁可以是公平的,通过参数设置;
6、代码量特别大的时候,我们一般使用Lock实现精准控制,Synchronized 适合代码量比较小的同步问题;
当有两个线程执行加减的时候,没有问题,但是当有四条线程交替执行的时候,会产生虚假唤醒的问题。此时在资源判断的时候,用while代替if
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 {
if (number!=0){ // 判断是否需要等待
this.wait();
}
number++; // 执行
System.out.println(Thread.currentThread().getName()+"\t"+number);
// 通知
this.notifyAll(); //唤醒所有线程
}
// -1
public synchronized void decrement() throws InterruptedException {
if (number==0){ // 判断是否需要等待
this.wait();
}
number--; // 执行
System.out.println(Thread.currentThread().getName()+"\t"+number);
// 通知
this.notifyAll(); //唤醒所有线程
}
}
package com.coding.demo01;
// Synchronized 版
/*
目的: 有两个线程:A B ,还有一个值初始为0,
实现两个线程交替执行,对该变量 + 1,-1;交替10次
传统的 wait 和 notify方法不能实现精准唤醒通知!
*/
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();
new Thread(()->{
for (int i = 1; i <=10 ; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
// -1
new Thread(()->{
for (int i = 1; i <=10 ; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 1; i <=10 ; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").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(); //唤醒所有线程
}
}
package com.coding.demo01;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
实现线程交替执行!
主要的实现目标:精准的唤醒线程!
三个线程:A B C
三个方法:A p5 B p10 C p15 依次循环
*/
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();
}
}
}
package com.coding.lock8;
import java.util.concurrent.TimeUnit;
/*
1、标准的访问情况下,先执行 sendEmail 还是 sendSMS
答案:sendEmail
被 synchronized 修饰的方式,锁的对象是方法的调用者,所以说这里两个方法调用的对象是同一个
先调用的先执行!
*/
public class LockDemo01 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(()->{
phone.sendEmail();
},"A").start();
//Thread.sleep(200);
TimeUnit.SECONDS.sleep(2);
new Thread(()->{
phone.sendSMS();
},"B").start();
}
}
class Phone{
public synchronized void sendEmail(){
System.out.println("sendEmail");
}
public synchronized void sendSMS(){
System.out.println("sendSMS");
}
}
此时先执行sendEmail,然后再执行sendSMS,同一个对象调用,按谁先调用谁先执行
package com.coding.lock8;
import java.util.concurrent.TimeUnit;
/*
2、sendEmail休眠3秒后 ,先执行 sendEmail 还是 sendSMS
答案:sendEmail
被 synchronized 修饰的方式,锁的对象是方法的调用者,所以说这里两个方法调用的对象是同一个
先调用的先执行!
*/
public class LockDemo02 {
public static void main(String[] args) throws InterruptedException {
Phone2 phone = new Phone2();
new Thread(()->{
try {
phone.sendEmail();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
//Thread.sleep(200);
TimeUnit.SECONDS.sleep(2);
new Thread(()->{
phone.sendSMS();
},"B").start();
}
}
class Phone2{
public synchronized void sendEmail() throws InterruptedException {
TimeUnit.SECONDS.sleep(3);
System.out.println("sendEmail");
}
public synchronized void sendSMS(){
System.out.println("sendSMS");
}
}
此时先执行sendEmail,然后再执行sendSMS
package com.coding.lock8;
import java.util.concurrent.TimeUnit;
/*
3、增加一个普通方法,请问先打印那个 sendEmail 还是 hello
答案:hello
新增加的这个方法没有 synchronized 修饰,不是同步方法,不受锁的影响!
*/
public class LockDemo03 {
public static void main(String[] args) throws InterruptedException {
Phone3 phone = new Phone3();
new Thread(()->{
try {
phone.sendEmail();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
//Thread.sleep(200);
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
phone.hello();
},"B").start();
}
}
class Phone3{
public synchronized void sendEmail() throws InterruptedException {
TimeUnit.SECONDS.sleep(4);
System.out.println("sendEmail");
}
// 没有 synchronized 没有 static 就是普通方式
public void hello(){
System.out.println("hello");
}
}
不按顺序执行
package com.coding.lock8;
import java.util.concurrent.TimeUnit;
/*
4、两个手机,请问先执行sendEmail 还是 sendSMS
答案:sendSMS
被 synchronized 修饰的方式,锁的对象是调用者;我们这里有两个调用者,两个方法在这里是两个锁
*/
public class LockDemo04 {
public static void main(String[] args) throws InterruptedException {
Phone4 phone1 = new Phone4();
Phone4 phone2 = new Phone4();
new Thread(()->{
try {
phone1.sendEmail();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
//Thread.sleep(200);
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
phone2.sendSMS();
},"B").start();
}
}
class Phone4{
public synchronized void sendEmail() throws InterruptedException {
TimeUnit.SECONDS.sleep(3);
System.out.println("sendEmail");
}
public synchronized void sendSMS(){
System.out.println("sendSMS");
}
}
两个调用者,增加了两把锁
package com.coding.lock8;
import java.util.concurrent.TimeUnit;
//LockDemo05.Class 模板,只有一个 static
//new LockDemo05(),可以创建多个对象
/*
5、两个静态同步方法,同一个手机请问先执行sendEmail 还是 sendSMS
答案:sendEmail
只要方法被 static 修饰,锁的对象就是 Class模板对象,这个则全局唯一!所以说这里是同一个锁
并不是因为synchronized
*/
public class LockDemo05 {
public static void main(String[] args) throws InterruptedException {
Phone5 phone = new Phone5();
new Thread(()->{
try {
phone.sendEmail();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
//Thread.sleep(200);
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
phone.sendSMS();
},"B").start();
}
}
class Phone5{
public static synchronized void sendEmail() throws InterruptedException {
TimeUnit.SECONDS.sleep(3);
System.out.println("sendEmail");
}
public static synchronized void sendSMS(){
System.out.println("sendSMS");
}
}
锁class,按顺序执行
package com.coding.lock8;
import java.util.concurrent.TimeUnit;
//LockDemo05.Class 模板,只有一个 static
//new LockDemo05(),可以创建多个对象
/*
6、两个静态同步方法,两个手机,请问先执行sendEmail 还是 sendSMS
答案:sendEmail
只要方法被 static 修饰,锁的对象就是 Class模板对象,这个则全局唯一!所以说这里是同一个锁
并不是因为synchronized
*/
public class LockDemo06 {
public static void main(String[] args) throws InterruptedException {
Phone6 phone = new Phone6();
Phone6 phone2 = new Phone6();
new Thread(()->{
try {
phone.sendEmail();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
//Thread.sleep(200);
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
phone2.sendSMS();
},"B").start();
}
}
class Phone6{
public static synchronized void sendEmail() throws InterruptedException {
TimeUnit.SECONDS.sleep(3);
System.out.println("sendEmail");
}
public static synchronized void sendSMS(){
System.out.println("sendSMS");
}
}
按顺序执行
package com.coding.lock8;
import java.util.concurrent.TimeUnit;
//LockDemo05.Class 模板,只有一个 static
//new LockDemo05(),可以创建多个对象
/*
7、一个普通同步方法,一个静态同步方法,只有一个手机,请问先执行sendEmail 还是 sendSMS
答案:sendSMS
synchronized 锁的是这个调用的对象
static 锁的是这个类的Class模板
这里是两个锁!
*/
public class LockDemo07 {
public static void main(String[] args) throws InterruptedException {
Phone7 phone = new Phone7();
new Thread(()->{
try {
phone.sendEmail();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
//Thread.sleep(200);
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
phone.sendSMS();
},"B").start();
}
}
class Phone7{
public static synchronized void sendEmail() throws InterruptedException {
TimeUnit.SECONDS.sleep(3);
System.out.println("sendEmail");
}
public synchronized void sendSMS(){
System.out.println("sendSMS");
}
}
不按顺序执行
package com.coding.lock8;
import java.util.concurrent.TimeUnit;
//LockDemo05.Class 模板,只有一个 static
//new LockDemo05(),可以创建多个对象
/*
7、一个普通同步方法,一个静态同步方法,两个手机,请问先执行sendEmail 还是 sendSMS
答案:sendSMS
synchronized 锁的是这个调用的对象
static 锁的是这个类的Class模板
这里是两个锁!
*/
public class LockDemo08 {
public static void main(String[] args) throws InterruptedException {
Phone8 phone = new Phone8();
Phone8 phone2 = new Phone8();
new Thread(()->{
try {
phone.sendEmail();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
//Thread.sleep(200);
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
phone2.sendSMS();
},"B").start();
}
}
class Phone8{
public static synchronized void sendEmail() throws InterruptedException {
TimeUnit.SECONDS.sleep(3);
System.out.println("sendEmail");
}
public synchronized void sendSMS(){
System.out.println("sendSMS");
}
}
不按顺序执行
List、Map、Set都是不安全的集合类
list不安全,Vector集合时安全的,但是效率低,
我们可以使用Collenctions集合工具类synchronizedList,
也可以使用JUC中的CopyOnWriteArrayList 都是线程安全的。
package com.coding.unsafe;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* 故障现象:ConcurrentModificationException 并发修改异常
* 导致原因:add方法没有锁!
* 解决方案:
* 1、List list = new Vector<>(); //jdk1.0 就存在的!效率低
* 2、List list = Collections.synchronizedList(new ArrayList<>());
* 3、List list = new CopyOnWriteArrayList<>();
*
* 什么是 CopyOnWrite; 写入是复制 (思想 COW)
* 多个调用者同时要相同的资源;这个有一个指针的概念。
* 读写分离的思想:
*/
public class UnSafeList {
public static void main(String[] args) {
// List list = Arrays.asList("a", "b", "c");
// list.forEach(System.out::println);
// List list = new ArrayList<>();
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 1; i <= 30; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,3));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
Set不安全,
我们可以使用Collenctions集合工具类synchronizedSet,
也可以使用JUC中的CopyOnWriteArraySet都是线程安全的。
package com.coding.unsafe;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
// ConcurrentModificationException
public class UnSafeSet {
public static void main(String[] args) {
// HashSet 底层是什么 就是 HashMap
// add,就是 HashMap 的 key;
Set<String> set = new HashSet<>();
// Set set = Collections.synchronizedSet(new HashSet<>());
// Set set = new CopyOnWriteArraySet();
for (int i = 1; i <=30 ; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,3));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
也可以使用JUC中的ConcurrentHashMap都是线程安全的。
package com.coding.unsafe;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
//ConcurrentModificationException
public class UnsafeMap {
public static void main(String[] args) {
// new HashMap<>() 工作中是这样用的吗? 不是
// 加载因子0.75f;,容量 16; 这两个值工作中不一定这样用!
// 优化性能!
// HashMap 底层数据结构,链表 + 红黑树
// = = = = = = =
// Map map = new HashMap<>();
Map<String, String> map = new ConcurrentHashMap<>();
// 人生如程序,不是选择就是循环,时常的自我总结十分重要!
for (int i = 1; i <=30 ; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,3));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}