多线程基础学习
一.创建线程、实现线程
有两种方法第一种是继承Thread类,第二种是实现Runnable接口。
1、继承Tread类
子类继承Thread类具备多线程功能
启动线程:子类对象.start()
不建议使用:避免OOP单继承局限性
例
/*
线程的学习
总结:线程开启不一定立即执行,需要看cpu的调度安排
在执行线程的时候调用start()方法来启动子线程,
这时候子线程,和主线程是交替之行的。
如果调用run()方法来启动线程,则是先执行子线程,
之后再来执行主线程。
*/
public class TestThread1 extends Thread {
@Override
public void run() {
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i=0;i<10;i++)
System.out.println("我在看代码---"+i);
}
public static void main(String[] args) {
TestThread1 testThread1 = new TestThread1();
testThread1.start();
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int j=0;j<20;j++)
System.out.println("我在学习多线程---"+j);
}
}
2、实现Runnable接口
实现接口Runnable具有多线程能力
启动线程:传入目标对象+Thread对象.start()
**推荐使用:避免单继承的局限性,灵活方便,方便同一个对象被多个线程使用 **
例:线程实现龟兔赛跑
import static java.lang.Thread.sleep;
/*
线程实现龟兔赛跑
*/
public class race implements Runnable {
private static String Winner;
@Override
public void run() {
for (int i=1;i<=100;i++){
//人为的然兔子每10步休息0.01s
if (Thread.currentThread().getName().equals("兔子")&&i%10==0) {
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//胜利者已经存在
if(GameOver(i)){
break;
}
System.out.println(Thread.currentThread().getName()+"-->走到了第"+i+"步");
}
}
//判单是否完成比赛的
private Boolean GameOver(int step){
if(Winner!=null){
return true;
} else if (step>=100){
Winner = Thread.currentThread().getName();
System.out.println("Winner is "+Winner);
return true;
}
return false;
}
public static void main(String[] args) {
race race= new race();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}
3、实现Callable接口
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
public class TestCallable implements Callable <Boolean> {
private String url;
private String name;
public TestCallable(String url,String name){
this.url=url;
this.name=name;
}
@Override
public Boolean call() {
new WebDownloder().download(url,name);
System.out.println("下载的图片名字为:"+name);
return true;
}
public static void main(String[] args) {
TestCallable t1 = new TestCallable("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fa4.att.hudong.com%2F27%2F67%2F01300000921826141299672233506.jpg&refer=http%3A%2F%2Fa4.att.hudong.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1614909229&t=d6c07074e94ca4465416b37e14f5cef0","1.jpg");
TestCallable t2 = new TestCallable("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fa0.att.hudong.com%2F52%2F62%2F31300542679117141195629117826.jpg&refer=http%3A%2F%2Fa0.att.hudong.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1614909230&t=6a6a762cda2412af43daff37427ecf4e","2.jpg");
TestCallable t3 = new TestCallable("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fa2.att.hudong.com%2F27%2F81%2F01200000194677136358818023076.jpg&refer=http%3A%2F%2Fa2.att.hudong.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1614909230&t=7016dfebce1f6b2db216e6f84c8693b7","3.jpg");
//创建执行服务 创建了3条线程
ExecutorService service = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> r1 = service.submit(t1);
Future<Boolean> r2 = service.submit(t2);
Future<Boolean> r3 = service.submit(t3);
//获取结果
try {
boolean rs1 = r1.get();
boolean rs2 = r2.get();
boolean rs3 = r3.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
//关闭服务
service.shutdownNow();
}
}
class WebDownloder{
public void download(String url,String name) {
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("Io异常");
}
}
}
4、静态代理模式
/*
总结:目标对象和代理对象要实现同一个接口
*/
public class StaticProxy {
public static void main(String[] args) {
WeeddingCompany w1 = new WeeddingCompany(new You());
w1.HappyMarry();
}
}
interface Marry {
void HappyMarry();
}
//真是角色
class You implements Marry {
@Override
public void HappyMarry() {
System.out.println("小张要结婚!!!");
}
}
//代理角色
class WeeddingCompany implements Marry {
private Marry target;
public WeeddingCompany(Marry target) {
this.target = target;
}
@Override
public void HappyMarry() {
befroe();
this.target.HappyMarry();
after();
}
private void befroe(){
System.out.println("布置婚礼现场!!!");
}
private void after(){
System.out.println("收结婚彩礼!!!");
}
}
5、Lamda表达式
/*
lambda表达是推到
*/
public class TestLamda {
//3.静态内部类
static class like implements ILkie{
@Override
public void lambda() {
System.out.println("I like lambda2");
}
}
public static void main(String[] args) {
ILkie like = new Like();
like.lambda();
ILkie like1 = new like();
like1.lambda();
//4.局部内部类
class Like2 implements ILkie {
@Override
public void lambda() {
System.out.println("I like lambda3");
}
}
like = new Like2();
like.lambda();
//5.匿名内部类,没有类的名称,必须借助接口或者父类
like = new ILkie() {
@Override
public void lambda() {
System.out.println("I like lambda4");
}
};
like.lambda();
//6.lambda表达方式
like = ()->{
System.out.println("I like lamnbda5");
};
like.lambda();
}
}
//1.定义一个接口
interface ILkie {
void lambda();
}
//2.实现类
class Like implements ILkie {
@Override
public void lambda() {
System.out.println("I like lambda1");
}
}
public class testlambda2 {
public static void main(String[] args) {
ILove love =null;
/* //1.lambda表示简化
ILove love = (int a)->{
System.out.println(a+b);
};
//简化1,参数类型简化
love = (a)->{
System.out.println(a);
};
//简化2,简化括号
love = a->{
System.out.println(a);
};*/
//简化3,去掉花括号
love = a -> System.out.println(a);
love.love(521);
/*
总结:lambda表达式只有一行代码的时候才能简化成一行
前提是接口为函数是接口
多个参数也可以去掉参数类型,要去掉都去掉,必须加上一个括号
*/
}
}
interface ILove{
void love(int a);
}
二. 线程同步(并发)
1、synchronized与Lock的对比
- Lock是显示锁(手动开启和关闭,别忘记关闭锁)synchronized是隐式锁,出了作用域自动释放。
- Lock只有代码快锁,synchronized有代码快锁和方法锁。
- 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好,并且具有更好的扩展性(提供更多的子类)
- 优先使用顺序:Lock > 同步代码块(已经进入了方法体,分配了相应资源)> 同步方法(在方法体之外)
2、 死锁
/*
死锁 死锁产生的四个条件
1.互斥条件:一个资源每次只能被一个进程使用
2.请求与保持条件:一个进程因请求资源而阻塞,对已获得的资源保持不放
3.不剥夺条件:进程以获得的资源,在未使用完之前,不能强行剥夺
4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
*/
public class DeadLock {
public static void main(String[] args) {
MakeUp g1 = new MakeUp(0,"晓丽");
MakeUp g2 = new MakeUp(1,"晓梅");
g1.start();
g2.start();
}
}
class Lipstick{//口红
}
class Mirror{//镜子
}
class MakeUp extends Thread{
//所需要的资源只有一份用static来修饰,保证只有一份
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice;//选择
String girlName;//女孩的姓名
MakeUp(int choice,String girlName){
this.choice = choice;
this.girlName =girlName;
}
@Override
public void run() {
//化妆
try {
makeup();
} catch (Exception e) {
e.printStackTrace();
}
}
//化妆 互相持有对方的锁,就是需要得对方的资源
private void makeup() throws Exception{
if (choice == 0){
synchronized (lipstick){//获得口红的锁
System.out.println(this.girlName+"持有口红锁");
Thread.sleep(1000);
//这样就造成了死锁
/* synchronized (mirror){//一秒钟后想要获得镜字锁
System.out.println(this.girlName+"获得镜子的锁");
}*/
}
//解决办法
synchronized (mirror){//一秒钟后想要获得镜字锁
System.out.println(this.girlName+"获得镜子的锁");
}
} else{
synchronized (mirror){//获得镜子的锁
System.out.println(this.girlName+"持有镜子锁");
Thread.sleep(2000);
/* synchronized (lipstick){//两秒钟后想要获得口红锁
System.out.println(this.girlName+"获得口红的锁");
}*/
}
synchronized (lipstick){//两秒钟后想要获得口红锁
System.out.println(this.girlName+"获得口红的锁");
}
}
}
}
3、lock锁
import java.util.concurrent.locks.ReentrantLock;
//测试lock锁
public class TestLock {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(ticket,"小明").start();
new Thread(ticket,"黄牛").start();
new Thread(ticket,"小红").start();
}
}
class Ticket implements Runnable {
int tichetNum = 10;
//定义lock锁
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
try {
Thread.sleep(1000);
BuyTicket();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void BuyTicket(){
while(true){
try {
lock.lock();//加锁
if (tichetNum > 0){
System.out.println(Thread.currentThread().getName()+"抢到了第"+tichetNum--+"张票!!");
} else {
break;
}
} finally {
lock.unlock();//解锁
}
}
}
}
4、生产者与消费者模型
4.1管程法
//测试:生产者消费者模型-->利用缓冲区解决:管程法
//生产者、消费者、产品、缓冲区
public class TestPC {
public static void main(String[] args) {
SynComtainer comtainer = new SynComtainer();
new Producer(comtainer).start();
new Consumer(comtainer).start();
}
}
//生产者
class Producer extends Thread {
SynComtainer comtainer;
public Producer (SynComtainer comtainer){
this.comtainer = comtainer;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("生产了"+i+"鸡");
comtainer.push(new Chicken(i));
}
}
}
//消费者
class Consumer extends Thread {
SynComtainer comtainer;
public Consumer (SynComtainer comtainer){
this.comtainer = comtainer;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("开始消费");
System.out.println("消费了--->"+comtainer.pop().id+"鸡");
}
}
}
//产品
class Chicken{
int id;
public Chicken(int id) {
this.id = id;
}
}
//缓冲区
class SynComtainer {
//缓冲区最多存放10个产品
Chicken[] chickens = new Chicken[10];
int count = 0;//存放产品的个数
//生产者生产产品
public synchronized void push(Chicken chicken){
//System.out.println("----->"+count);
//判断缓冲区是否满了,如果满了, -停止生产,开始消费
if(count ==chickens.length-1){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果可以,就生产产品放入缓冲区
chickens[count] = chicken;
count++;
this.notifyAll();//唤醒同一个对象上所调用wait()方法的线程,优先级别高得先执行
}
//消费者消费产品
public synchronized Chicken pop(){
//如果产品被消费完了就通知生产者开始生产
if (count==0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果可以,就消费
count--;
Chicken chicken = chickens[count];
this.notifyAll();
return chicken;
}
}
4.2 信号灯法
//测试生产者消费者模式:信号灯发,通过标志位来解决
public class TestPC2 {
public static void main(String[] args) {
TV tv = new TV();
new Performer(tv).start();
new Audience(tv).start();
}
}
//生产者->演员
class Performer extends Thread{
TV tv;
public Performer(TV tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if(i%2==0){
tv.Play("快乐大本营播放中!!!");
} else {
tv.Play("抖音-->记录美好生活!!!");
}
}
}
}
//消费者->观众
class Audience extends Thread{
TV tv;
public Audience(TV tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
tv.watch();
}
}
}
//产品->节目
class TV{
String program;//节目
Boolean flag = true; //T为生产节目,F为播放节目
public synchronized void Play(String program){
if(flag==false){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("演员表演了:"+program);
this.notifyAll();//通知唤醒观看线程
this.program = program;
this.flag = false;
}
public synchronized void watch(){
if(flag==true){
try {
this.wait();//线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观众观看了:"+program);
this.notifyAll();
this.flag = true;
}
}
5、线程同步之synchronized锁
/*
不安全的取钱
synchronized 默认锁的是类本身
synchronized (){} 同步块可以锁任何对象
锁一定要锁变化的量。
加上synchronized是为了让线程更加安全
*/
public class UnSafeBank {
public static void main(String[] args) {
Account account = new Account(100,"儿子上学基金");
Bank you = new Bank(account,80,"you");
Bank girlfriend = new Bank(account,100,"girlfriend");
you.start();
girlfriend.start();
}
}
class Account {//账户
int balanceMoney; //余额
String name; //姓名
public Account(int balanceMoney, String name) {
this.balanceMoney = balanceMoney;
this.name = name;
}
}
/*
模拟在银行里边取钱
*/
class Bank extends Thread {
private int drawingMoney; //需要取出的钱
private int NowMoney; //现在手里的钱
Account account;//账户
public Bank (Account account,int drawingMoney,String name){
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
public void run(){
//synchronized 关键字是为了让线程变得安全
synchronized (account){
//判断卡里的余额是否够
if(account.balanceMoney-drawingMoney<0){
System.out.println(Thread.currentThread().getName()+"钱不够,取不了了");
return ;
}
//模拟网络延时放大问题发生的概率
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卡里剩余的余额 = 余额 - 取出的钱
account.balanceMoney = account.balanceMoney-drawingMoney;
//现在手里的钱
NowMoney = NowMoney + drawingMoney;
//Thread.currentThread().getName() <=> this.getName;
System.out.println(this.getName()+"取走的钱为:"+NowMoney);
System.out.println(account.name+"余额为:"+account.balanceMoney);
}
}
}
6、不安全的list
import java.util.ArrayList;
import java.util.List;
import static java.lang.Thread.sleep;
/*
不加sleep可能导致其它线程没有跑完,但是主线程跑完了。
这也就是为啥把sleep去掉之后加上synchronized而导致不够10000
*/
public class UnSafeList {
public static void main(String[] args) {
List <String> list = new ArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
synchronized (list) {
list.add(Thread.currentThread().getName());
}
}).start();
}
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}