JUC多线程
java默认有几个线程?
2个 一个是main 一个是GC(垃圾回收)。
java自己可以开启线程吗?
Java不可以开启线程 ,只能通过本地方法调用,Java无法直接操作硬件。
并发与并行
并发:同一时间间隔 多个线程交替执行
并行:多个线程同时执行
线程有6个状态:
- NEW 新生
- RUNNABLE 运行
- BLOCKED 阻塞
- WATING 等待
- TIMED_WATING 超时等待
- TEMINATED 终止
wait和sleep的区别
1、来自不同的类
wait来自Object
sleep来自Thread
2、关于锁的释放
wait会释放锁,sleep睡觉了,抱着锁睡觉,不会释放。
3、使用的范围不同
wait必须在同步代码块中
sleep可以在任何地方
4、是否需要捕获异常
wait不需要捕获异常
sleep必须要捕获异常
锁
传统Synchronized
不加锁的情况:
结果会乱序
给sale方法加上锁
结果正常。
public class saleTicketDemo01 {
public static void main(String[] args) {
//并发: 多线程操作同一个资源类,把资源类丢入线程
final Ticket ticket = new Ticket();
new Thread(()->{//lambda表达式 (参数)->{}
for (int i=0;i<50;i++)
ticket.sale();
},"A").start();
new Thread(()->{
for (int i=0;i<50;i++)
ticket.sale();
},"B").start();
new Thread(()->{
for (int i=0;i<50;i++)
ticket.sale();
},"C").start();
}
}
class Ticket{
//票的数量
private int num = 30;
//卖票的操作
public synchronized void sale(){
if(num>0)
System.out.println("卖出第"+(30-num+1)+"张票,还剩"+(--num)+"票");
}
}
Lock锁
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class saleTicketDemo02 {
public static void main(String[] args) {
//并发: 多线程操作同一个资源类,把资源类丢入线程
final Ticket2 ticket = new Ticket2();
new Thread(()->{//lambda表达式 (参数)->{}
for (int i=0;i<50;i++)
ticket.sale();
},"A").start();
new Thread(()->{
for (int i=0;i<50;i++)
ticket.sale();
},"B").start();
new Thread(()->{
for (int i=0;i<50;i++)
ticket.sale();
},"C").start();
}
}
class Ticket2{
//票的数量
private int num = 30;
private Lock lock = new ReentrantLock();
//卖票的操作
public void sale(){
lock.lock();//上锁
try{
if(num>0)
System.out.println("卖出第"+(30-num+1)+"张票,还剩"+(--num)+"票");
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();//解锁
}
}
}
Synchronized和Lock的区别
1、Synchronized是java内置的关键字,Lock是一个java的类
2、Synchronized 无法判断获取锁的状态 Lock可以判断是否获取到了锁
3、Synchronized 会自动释放锁, Lock必须要手动释放锁,如果不释放锁,死锁
4、Synchronized 线程1(获得锁)、线程2(等待,傻傻的等待) Lock锁不一定会等待下去
5、Synchronized 可重入锁不可以中断,非公平; Lock 可重入的,可以判断锁,可以自己设置公不公平
6、Synchronized 适合锁少量的代码同步问题,Lock适合锁大量的同步代码。
生产者消费者问题
传统Synchronized实现
public class demo01 {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
//信号量 资源类
class Data{
private int num = 0;
//生产者
public synchronized void increment() throws InterruptedException {
if(num!=0){
//等待
this.wait();
}
num++;
System.out.println(num);
//唤醒
this.notifyAll();
}
//消费者
public synchronized void decrement() throws InterruptedException {
if(num==0){
//等待
this.wait();
}
num--;
System.out.println(num);
//唤醒
this.notifyAll();
}
}
存在的问题
两个线程没有问题,多个线程就会出现问题
防止虚假唤醒,等待需要放在循环中
将if 改为while判断
JUC版的生产者消费者
通过lock 获得 conditon
condition.await() 等待
condition.signalAll() 唤醒全部
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class demo02 {
public static void main(String[] args) {
Data2 data = new Data2();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.increment();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.decrement();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.increment();
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.decrement();
}
},"D").start();
}
}
//信号量 资源类
class Data2{
private int num = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
//生产者
public void increment() {
lock.lock();
try{
while(num!=0){
//等待
condition.await();
}
num++;
System.out.println(Thread.currentThread().getName()+"=>"+num);
//唤醒
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
//消费者
public synchronized void decrement(){
lock.lock();
try{
while(num==0){
//等待
condition.await();
}
num--;
System.out.println(Thread.currentThread().getName()+"=>"+num);
//唤醒
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
问题:线程乱序的状态
要使它能够有序的执行 A B C D
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class demo03 {
public static void main(String[] args) {
Data3 data = new Data3();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.printA();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.printB();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.printC();
}
},"C").start();
}
}
class Data3{
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 printA(){
lock.lock();
try {
while(num!=1){
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"=>AAAA");
num=2;
condition2.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
while(num!=2){
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"=>BBBB");
num=3;
condition3.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
while(num!=3){
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"=>CCCC");
num=1;
condition1.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
八锁问题
- 锁的对象是方法的调用者
- 不是同步方法不受锁定的影响
- static静态方法 类一加载就有了 锁的是Class类模板 (唯一的)
import java.util.concurrent.TimeUnit;
//1,标准情况下,两个线程先打印 发短信 还是 打电话? 发短信
//2,标准情况下,发短信方法sleep 4s 两个线程先打印 发短信 还是 打电话? 发短信
public class test01 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(()->{
phone.sendSms();
}).start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
phone.call();
}).start();
}
}
class Phone{
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
因为锁的对象 是 方法的调用者, 两个方法用的是同一个锁。谁先拿到就谁先执行
import java.util.concurrent.TimeUnit;
//3,增加一个普通方法,两个线程先打印 发短信 还是 hello? hello
public class test02 {
public static void main(String[] args) throws InterruptedException {
Phone2 phone = new Phone2();
new Thread(()->{
phone.sendSms();
}).start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
phone.hello();
}).start();
}
}
class Phone2{
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
public void hello(){
System.out.println("hello");
}
}
因为普通方法没有锁,不是同步方法,不受锁的影响
import java.util.concurrent.TimeUnit;
//4,两个对象 都是同步方法 先打印 发短信 还是 打电话? 打电话
public class test02 {
public static void main(String[] args) throws InterruptedException {
Phone2 phone1 = new Phone2();
Phone2 phone2 = new Phone2();
new Thread(()->{
phone1.sendSms();
}).start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
phone2.call();
}).start();
}
}
class Phone2{
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
因为现在是两个对象,发短信锁的是phone1对象,打电话锁的是phone2对象,是两把锁
import java.util.concurrent.TimeUnit;
//5,两个静态同步方法 先打印 发短信 还是 打电话? 发短信
public class test03 {
public static void main(String[] args) throws InterruptedException {
Phone3 phone = new Phone3();
new Thread(()->{
phone.sendSms();
}).start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
phone.call();
}).start();
}
}
//只有唯一的一个Class对象
class Phone3{
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void call(){
System.out.println("打电话");
}
因为static 静态同步方法 锁的是Class类模板,所以发短信方法先拿了,打电话只能等待
import java.util.concurrent.TimeUnit;
//6,两个静态同步方法,两个对象 先打印 发短信 还是 打电话? 发短信
public class test03 {
public static void main(String[] args) throws InterruptedException {
//两个对象只有一个Class
Phone3 phone1 = new Phone3();
Phone3 phone2 = new Phone3();
new Thread(()->{
phone1.sendSms();
}).start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
phone2.call();
}).start();
}
}
class Phone3{
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void call(){
System.out.println("打电话");
}
}
static同步方法锁的是Class ,只有一把锁 所以phone1先拿了,先打印发短信
import java.util.concurrent.TimeUnit;
//7,一个普通同步方法,一个静态同步方法, 先打印 发短信 还是 打电话? 打电话
public class test04 {
public static void main(String[] args) throws InterruptedException {
Phone4 phone = new Phone4();
new Thread(()->{
phone.sendSms();
}).start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
phone.call();
}).start();
}
}
class Phone4{
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
因为这里是两把锁,静态同步方法锁的是Class 普通同步方法锁的是调用者
import java.util.concurrent.TimeUnit;
//8,一个普通同步方法,一个静态同步方法,两个对象 先打印 发短信 还是 打电话? 打电话
public class test04 {
public static void main(String[] args) throws InterruptedException {
Phone4 phone1 = new Phone4();
Phone4 phone2 = new Phone4();
new Thread(()->{
phone1.sendSms();
}).start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
phone2.call();
}).start();
}
}
class Phone4{
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
与7同理,两个用的不是同一个锁