多线程基础
目录
一、初识多线程
普通方法和多线程:
进程process和线程thread
创建线程的方式
通过继承Tread类
实战演示:
package com.yan;
/**
* @author :Yan Guang
* @date :Created in 2021/1/12 18:57
* @description: 单核cpu只能实现单线程,因为cpu执行速度很快,
* 所以这里是视作为多线程,但是实际上还是交替执行的(由CPU调度实现),并不是一起执行的;
* 但是由于这个时间很短很短,所以看起来可以是作为是同步执行的
*/
public class demo01 extends Thread{
@Override
public void run() {
//run方法线程
for (int i = 0; i < 20; i++) {
System.out.println("run线程~~~~~~~~~~~~~~~~");
}
}
public static void main(String[] args) {
demo01 thread1 = new demo01();
//这里相当于开辟了一条新的线程,但是默认会执行run方法
thread1.start();
//main主线程,两条线程是同时执行,交替执行的
for (int i = 0; i < 1000; i++) {
System.out.println("main线程~");
}
}
}
搞完收工~
通过实现Runnable接口
package com.yan;
/**
* @author :Yan Guang
* @date :Created in 2021/1/12 19:53
* @description:
*/
public class demo2 implements Runnable{
@Override
public void run() {
//run方法线程
for (int i = 0; i < 20; i++) {
System.out.println("run线程~~~~~~~~~~~~~~~~");
}
}
public static void main(String[] args) {
//这里跟demo1里面的不一样,在Thread构造方法里面加入了Runnable参数
new Thread(new demo2()).start();
//main主线程,两条线程是同时执行,交替执行的
for (int i = 0; i < 1000; i++) {
System.out.println("main线程~");
}
}
}
这里的Thread类也实现了Runnable接口~继承Thread的时候不一定非要重写run方法,是因为start()方法的调用不得不使用run()方法,没有目标target则无法用start()方法,还有一种方法是实现Callable接口,但是用的不多,所以在这里就不演示了
数据冲突问题
package com.yan;
/**
* @author :Yan Guang
* @date :Created in 2021/1/14 20:08
* @description:
*/
public class demo3 implements Runnable{
private int tickName = 10;
@Override
public void run() {
while (true) {
if (tickName <= 0) {
break;
}
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-->拿到了第" + tickName-- + "票");
}
}
public static void main(String[] args) {
demo3 ticket = new demo3();
new Thread(ticket,"小黄").start();
new Thread(ticket,"小名").start();
new Thread(ticket,"小红").start();
}
}
多个线程操作同一个资源的情况下,线程不安全,并发问题(在同一个CPU下,多个线程同时发起的任务请求,出现的问题就叫并发问题),数据紊乱。
二、深入了解Thread类
静态代理
这里首先you和weddingCompany是接口wedding的实现类,然后weddingCompany通过构造方法,需要传入一个you对象,从而代理可以帮you对象做一些事情,这就是静态代理;同理我们可以对比一下创造一个线程的时候,因为Thread是继承Runnable接口的,然后传入的参数类型也是Runnable接口类型,相当于是代理模式,Thread代理了Runnable接口实现了里面的方法,跟这里的weddingCompany代理you实现了里面的方法是一个道理。
三、Lamda表达式
简介
函数式接口
静态内部类
局部内部类
匿名内部类
lamda表达式
其实也就是把匿名内部类稍有改变,就是把new 实现类名给省略了,然后直接写函数式接口里面唯一的一个方法里面的内容就可以了,参数类型也可以省略
总结
四、线程的五大状态(生命周期)
线程的方法
线程停止的方法
线程礼让
记住:礼让不一定成功,看CPU心情
线程休眠
模拟网络延时:放大问题的发生性
Join
因为这里是并发执行的,所以main会先跑,然后我们这里用join方法让线程插队进来了
线程状态观测
死亡之后的线程不能再次启动了
线程的优先级
守护线程
守护线程可以理解为守护我们程序正常执行的线程,不用去管它们,我们可以通过setDaemon方法把用户线程升级为守护线程,虚拟机只用保证把用户线程执行完就可以了,守护线程不需要执行完
五、线程同步
介绍
发生在多个线程操作一个资源
并发就是多线程去抢占一个资源(包括CPU)
同步就是排队机制,线程一个个进入队列,资源每次都是一对一的跟线程进行操作,同步就是由队列+锁构成的
线程不安全例子
package com.yan.syn;
/**
* @author :Yan Guang
* @date :Created in 2021/1/17 10:42
* @description:
*/
public class UnSafeBuyTicket {
public static void main(String[] args){
BuyTicket station = new BuyTicket();
new Thread(station,"小红").start();
new Thread(station,"小章").start();
new Thread(station,"小蓝").start();
}
}
class BuyTicket implements Runnable{
private int ticketNums=10;
boolean flag=true;
@Override
public void run() {
while (flag){
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void buy() throws InterruptedException {
if (ticketNums<=0){
flag=false;
return;
}
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
}
}
package com.yan.syn;
/**
* @author :Yan Guang
* @date :Created in 2021/1/17 11:04
* @description:
*/
public class UnSafeBank {
public static void main(String[] args) {
Account account = new Account(100, "结婚基金");
GetMoney you = new GetMoney(account,50,"你");
GetMoney youWife = new GetMoney(account,100,"你妻子");
you.start();
youWife.start();
}
}
class Account{
int money;
String name;
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
class GetMoney extends Thread{
private Account account;
private int drawingMoney;//取了多少钱
private int nowMoney;
public GetMoney(Account account, int drawingMoney,String name) {
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
@Override
public void run() {
if (account.money-drawingMoney<0){
System.out.println(Thread.currentThread().getName()+"卡里的钱不够了");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money = account.money-drawingMoney;
nowMoney=nowMoney+drawingMoney;
System.out.println(account.name+"余额为"+account.money);
System.out.println(Thread.currentThread().getName()+"手里还剩余的钱为"+nowMoney);
}
}
public class UnSafeArrayList {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<>();
for (int i = 0; i < 50000; i++) {
new Thread(()-> {
list.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(1000);
System.out.println(list.size());
}
}
可能在同一个index上后面进来的数据把前面的给覆盖掉了,所以线程是不安全的
解决办法
锁的是方法的调用者(对象),休眠状态不会释放锁(继续保持锁住状态),这里是三个线程操作一个对象,调用方法锁的是对象车站,将这一个车站的数据都锁住了
这里两个线程进行操作,锁不住,多个线程只会锁住当前实例对象,这里是两个线程各自操作各自的对象,锁方法锁的是调用者,所以锁的是银行,而不是账户,所以我们需要将账户进行加锁,使用同步块
锁的对象就是变化的量,需要增删改的量
这里也是一个道理,把改变的变量锁住就可以了,这里改变的是list集合对象,锁住就完事儿了
ArrayList是线程不安全的,CopyOnWriteArrayList是线程安全的。
六、死锁
介绍
解决办法
小结
加上synchronized同步块的资源每次进来的对象都需要带着锁,当有两把锁的时候,锁住了当前的资源别人想要得到你的资源的时候,你却锁住了不放,就造成了死锁,这里的解决办法就是把我当前资源锁住之后,锁完就放,然后再去找你要你的东西就解决了,synchronized同步块运行结束之后就会释放锁,这里还没有释放锁就想着就找别人要资源,别人又要你的资源,东西就都锁住了,谁也不愿意释放,就死锁了,放在外面就相当于释放了我的东西了。
锁
介绍
对比
七、线程协作(生产消费者模式)
解决方式一
package com.yan.gaoji;
import com.sun.org.apache.bcel.internal.generic.PUSH;
/**
* @author :Yan Guang
* @date :Created in 2021/1/17 12:14
* @description:
*/
public class TestPC {
public static void main(String[] args) {
hunCun hunCun = new hunCun();
new Productor(hunCun).start();
new Customer(hunCun).start();
}
}
//生产者只顾生产,一直往缓存区里面加鸡
class Productor extends Thread{
hunCun chun;
public Productor (hunCun hunCun){
this.chun=hunCun;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("生产了"+i+"只鸡");
try {
chun.push(new Chicken(i));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//消费者只顾消费,一直往缓存区里面取鸡
class Customer extends Thread{
hunCun chun;
public Customer (hunCun hunCun){
this.chun=hunCun;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
System.out.println("消费了"+chun.pop().no+"只鸡");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Chicken {
int no;
public Chicken(int no) {
this.no = no;
}
}
class hunCun{
Chicken[] chickens = new Chicken[10];
int count =0;
public synchronized void push(Chicken chicken) throws InterruptedException {
if (count==chickens.length){
//通知生产者等待
this.wait();
}
chickens[count]=chicken;
count++;
//如果缓存区没有满,就唤醒生产者继续生产
this.notify();
}
public synchronized Chicken pop() throws InterruptedException {
if (count==0){
//让消费者等待
this.wait();
}
count--;
//通知消费者可以消费了
this.notify();
return chickens[count];
}
}
解决方式二
package com.yan.gaoji;
/**
* @author :Yan Guang
* @date :Created in 2021/1/17 12:41
* @description:
*/
public class TestPC2 {
public static void main(String[] args) {
Chicken2 chicken2 = new Chicken2();
new Product(chicken2).start();
new Eat(chicken2).start();
}
}
//生产者只顾生产,一直往缓存区里面加鸡
class Product extends Thread{
Chicken2 chicken2;
public Product(Chicken2 chicken2){
this.chicken2=chicken2;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
chicken2.zuo();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//消费者只顾消费,一直往缓存区里面取鸡
class Eat extends Thread{
Chicken2 chicken2;
public Eat(Chicken2 chicken2){
this.chicken2=chicken2;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
chicken2.eat("一只小小鸡");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Chicken2 {
String eat;
//如果为真,就吃,不能做,为假就做,不能吃
boolean flag=true;
public synchronized void eat(String eat) throws InterruptedException {
//让消费者先不吃,生产者做
if (!flag){
this.wait();
}
System.out.println("吃了"+eat);
//让消费者吃
this.notifyAll();
this.eat=eat;
flag=!flag;
}
public synchronized void zuo() throws InterruptedException {
//让生产者先不做,消费者吃
if (flag){
this.wait();
}
System.out.println("做了"+eat);
//让生产者做,消费者吃
this.notifyAll();
flag=!flag;
}
}
线程池
线程池创建的方法
public class TestMoreTread {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}