多线程

多线程   java.Thread

1、线程简介

任务、进程、线程、多线程、

process进程 thread 线程

程序执行一次就形成了进程,进程里可以包含若干个进程

概念

进程就是独立的执行路径

在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程main,gc(垃圾回收)线程

main()称之为主线程,为系统的入口,用于执行整个程序

在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系用紧密联系的,先后顺序是不能人为干预的。

对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制

线程会带来额外的开销,如cpu调度时间,并发控制开销

每个线程在自己的工作内存交互,互存控制不会造成数据不一致

2、线程创建

1、集成thread类

2、实现Runnable接口

3、实现Callable接口

Thread

package Thread;
//创建线程方式:继承Thread类,重写run()方法,调用start开启线程
public class Thread01 extends Thread {
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码---" + i);
}
}

public static void main(String[] args) {
    //main线程,主线程

    Thread textThread1 = new Thread();

    //调用start方法
    textThread1.start();
    for (int i = 0; i < 20; i++) {
        System.out.println("我在学习多线程---" + i);
    }
}

}
注意:线程开启不一定立即执行,由cpu调度

实现Runnable接口

package Thread;
//创建线程方式2,实现runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start方法
public class Thread02 implements Runnable{
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码---" + i);
}
}

public static void main(String[] args) {
    //创建一个runnable接口的实现类对象
    Thread02 thread02 = new Thread02();
    new Thread(thread02).start();
    for (int i = 0; i < 20; i++) {
        System.out.println("我在学习多线程---" + i);
    }
}

}
结伦

继承Thress类

1,子类继承Thress类具备多线程能力

2,启动线程:子类对象.start();

3,不建议使用:避免oop单继承局限性

实现Runnable接口

1,实现接口Runnable具备多线程能力

2,启动线程:传入目标对象+Thread对象.start();

3,推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

并发问题

package Thread;
//多个线程同时套做一个对象
public class TestThread04 implements Runnable{
private int ticketNums = 10;

@Override
public void run() {
    while (true){
        if(ticketNums <= 0){
            break;
        }
        System.out.println(Thread.currentThread().getName()+ "拿到了第" + ticketNums-- + "票");
    }

}
public static void main(String[] args) {
    TestThread04 ticket = new TestThread04();
    new Thread(ticket,"小明").start();
    new Thread(ticket,"小明").start();
    new Thread(ticket,"小明").start();
}

}
龟兔赛跑

package Thread;

public class Race implements Runnable {
private static String winner;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {

        if (Thread.currentThread().getName().equals("兔子") && i%10 == 0){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        boolean flag = gameOver(i);
        if(flag){
            break;
        }
        System.out.println(Thread.currentThread().getName() + "跑了" + i +"步");
        }
    }
private boolean gameOver(int steps) {
    if (winner != null) {
        return true;
    } else {
        if (steps >= 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接口

4、静态方法

package Thread;

public class Agent{
public static void main(String[] args) {
new WeddingCompany(new You()).HappyMarry();
//WeddingCompany weddingCompany = new WeddingCompany(new You());
//weddingCompany.HappyMarry();
}
}
interface Marry{
void HappyMarry();
}
class You implements Marry{

@Override
public void HappyMarry() {
    System.out.println("要结婚了");
}

}
class WeddingCompany implements Marry{
private Marry target;

public WeddingCompany(Marry target) {
    this.target = target;
}

@Override
public void HappyMarry() {
    before();
    this.target.HappyMarry();
    after();
}

private void after() {
    System.out.println("结束之后收尾款");
}

private void before() {
    System.out.println("结婚之前,布置现场");
}

}
5、Lamds

package Thread;

public class TestLambda {

//3、静态内部类
static class Like2 implements ILike {
    @Override
    public void Lambda() {
        System.out.println(" i like lambda2");
    }
}

public static void main(String[] args) {
    ILike like = new Like();
    like.Lambda();

    like = new Like2();
    like.Lambda();

    //4局部内部类
    class Like3 implements ILike {
        @Override
        public void Lambda() {
            System.out.println(" i like lambda3");
        }
    }
    like = new Like3();
    like.Lambda();


    //匿名内部类
    like = new ILike() {
        @Override
        public void Lambda() {
            System.out.println(" i like lambda4");
        }
    };
    like.Lambda();

    //Lambda
    like = ()-> {
        System.out.println(" i like lambda5");
    };
    like.Lambda();

}

}
//1定义一个函数是接口
interface ILike {
void Lambda();
}
//2实现类
class Like implements ILike {
@Override
public void Lambda() {
System.out.println(" i like lambda");
}
}
6、线程停止

setPriority( int newPriority)更改线程的优先级

staatic void sleep(long millis)在指定的毫秒内让当前正在执行的线程休眠

void join() 等待该线程终止

static void yield()暂停当前正在执行的线程对象,并执行其他线程

void interrupt()中断线程,别用这个方式

boolean isAlive()测试线程是否出入活动状态

推荐线程自己停下来,建议使用一个标志位进行种植变量当flag=false ,则终止线程执行

package Thread;

public class TestStop implements Runnable{
//1\设置标识为
private boolean flag = true;
public static void main(String[] args) {
TestStop testStop = new TestStop();

    new Thread(testStop).start();

    for (int i = 0; i < 1000; i++) {
        System.out.println("main" + i);
        if(i == 900){
            //调用stop方法切换标识位,让线程停止
            testStop.stop();
            System.out.println("该停止了");
        }
    }
}
//2设置一个公开的停止线程的方法
public void stop(){
    this.flag = false;
}
@Override
public void run() {
    int i = 0;
    while (flag){
        System.out.println("run.....Thread" + i++);
    }
}

}
7、sleep 线程休眠

sleep(时间)制定当前线程诅塞的毫秒数;

sleep 存在异常interruptedException;

sleep时间达到后线程进入就绪状态

sleep可以模拟网络延时,倒计时等;

每一个对象都有一锁,sleep不会释放锁

package Thread;
//模拟网络延时,放大问题的发生性
public class TestSleep implements Runnable {
private int ticketNums = 10;

@Override
public void run() {
    while (true){
        if(ticketNums <= 0){
            break;
        }
        System.out.println(Thread.currentThread().getName()+ "拿到了第" + ticketNums-- + "票");
    }
    try {
        Thread.sleep(100);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
public static void main(String[] args) {
    TestSleep ticket = new TestSleep();
    new Thread(ticket,"小明").start();
    new Thread(ticket,"小明").start();
    new Thread(ticket,"小明").start();
}

}

package Thread;

public class TestSleep2 {
public static void main(String[] args) {
try {
tenDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

//倒计时
public static void tenDown() throws InterruptedException {
int num = 10;
while (true){
Thread.sleep(1000);
System.out.println(num--);
if (num <= 0){
break;
}
}
}
}
8、线程礼让yield

礼让线程,让当前正在执行的线程停止,但不阻塞

将线程从运行状态转为就绪状态

让cpu重新调度,礼让不一定成功,看cpu心情

package Thread;
//li让线程,不一定成功
public class TextYield {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield, "a").start();
new Thread(myYield, "b").start();
}

static class MyYield implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "线程开始执行");
        Thread.yield();//礼让
        System.out.println(Thread.currentThread().getName() + "线程停止执行");
    }
}

}
8、合并线程join

join合并线程,待次线程执行完成后,在执行其他线程,其他线程阻塞(就是插队)

package Thread;

//测试join方法//想象成插队
public class TextJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("线程vip来了" + i);
}
}

public static void main(String[] args) throws InterruptedException {
    TextJoin textJoin = new TextJoin();
    Thread thread = new Thread(textJoin);
    thread.start();
    //启动
    for (int i = 0; i < 500; i++) {
        if(i ==200) {
            thread.join();
        }
        System.out.println("main" + i);
    }

}

}
9、线程状态观测thread.getState();

new:尚未启动的线程处于此状态

Runnable:在java虚拟机中执行的线程

Blicked:被阻塞等待监听器锁定的线程

Waiting:正在等待另一个程序执行特定的动作的线程

Timed-Waitting:正在等待另一个线程执行特定的动作达到指定等待时间的线程

Terminated:已终止的线程

package Thread;

public class TextState {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("///////");
});

    //观察状态
    Thread.State state = thread.getState();
    System.out.println(state);



    //观察启动后
    thread.start();
    state = thread.getState();
    System.out.println(state);


    while (state != Thread.State.TERMINATED){//只要线程不停止,就一直输除状态
        Thread.sleep(1000);
        state = thread.getState();//更新
        System.out.println(state);
    }
}

}
线程一旦结束,死亡就不能启动了

9、线程的优先级priority

java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级的决定应该调度那个线程来执行

getPriority().setPriovrity(int xxx);

package Thread;

public class TestPriority {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());

    MyPriority myPriority = new MyPriority();

    Thread ti = new Thread(myPriority);
    Thread t2 = new Thread(myPriority);
    Thread t3 = new Thread(myPriority);
    Thread t4 = new Thread(myPriority);
    Thread t5 = new Thread(myPriority);
    Thread t6 = new Thread(myPriority);

    ti.start();

    t2.setPriority(2);
    t2.start();

    t3.setPriority(4);
    t3.start();

    t4.setPriority(Thread.MAX_PRIORITY);
    t4.start();

    t5.setPriority(1);
    t5.start();

    t6.setPriority(3);
    t6.start();

}

}
class MyPriority implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());
}
}
优先级低只是意味着获得的调度概率低,并不是优先级最低就不会被调用了,这都是看cpu的调度(性能倒至)

10、守护线程daemon

线程分为用户线程和守护线程

虚拟机必须确保用户线程执行完毕

虚拟机不用等待守护线程执行完毕

package Thread;

public class TestDaemon {
public static void main(String[] args) {
God god = new God();
Yo yo = new Yo();
Thread thread = new Thread(god);
thread.setDaemon(true);//默认是false是用户线程
thread.start();
new Thread(yo).start();
}
}

class God implements Runnable{
@Override
public void run() {
while (true){
System.out.println("上帝一直活着");
}

}

}

class Yo implements Runnable{

@Override
public void run() {
    for (int i = 0; i < 36500; i++) {
        System.out.println("活着");
    }
    System.out.println("死去");
}

}
11、线程同步机制

多个线程操作同一个资源

锁 synchronized   存在的问题:一个线程持有锁会导致其他所有需要此锁的线程挂起;在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题;如果一个优先级高的线程等待一个优先级低的线程释放锁胡导致优先级倒置,引起性能问题。

同步方法

1、由于我们可以通过private关键字来保证数据对象只能被方法访问,所以我们只需要针对方法提出一套机制,这套机制就是synchronized关键字,他包含两种用法:synchronized方法和synchronizeds块

2、synchronized方法控制对“对象”的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行。 缺陷:若将一个大的方法声明为synchronized将会影响效率

package Thread;

public class UnsafeBank {
public static void main(String[] args) {
//账户
Account account = new Account(100,"基金");
Drawing you = new Drawing(account,50,"你");
Drawing wo = new Drawing(account,100,"我");

    you.start();
    wo.start();
}

}

//账号
class Account{
int money;
String name;
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}

//银行模拟取款
class Drawing extends Thread{
Account account;//账户

//取了多少钱
int drawingMoney;
//现在手里多少钱
int nowMoney;

public Drawing(Account account,int drawingMoney,String name){
    super(name);
    this.account = account;
    this.drawingMoney = drawingMoney;

}

//取钱
@Override
public void run() {

    synchronized(account){
        //判断有没有钱
        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(this.getName() + "手里的钱:" +nowMoney);

    }

}

}
package Thread;

import java.util.concurrent.CopyOnWriteArrayList;

public class Test {
public static void main(String[] args) {
CopyOnWriteArrayList list = new CopyOnWriteArrayList();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}

    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(list.size());
}

}
12、死锁

多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形,某一个同步块同时拥有“两个以上对象的锁”时,就可能出现“死锁”的问题

package Thread;

import com.sun.deploy.security.SelectableSecurityManager;

//死锁:多个线程互相抱着对方的资源,然后形成死锁
public class DeadLock {
public static void main(String[] args) {
Makeup g1 = new Makeup(0,"11");
Makeup g2 = new Makeup(1,"22");
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 (InterruptedException e) {
        e.printStackTrace();
    }
}

//化妆,互相持有对方的锁
private void makeup() throws InterruptedException {
    if (choice == 0) {
        synchronized (lipstick) {//获得口红的锁
            System.out.println(this.girlName + "获得口红的锁");
            Thread.sleep(1000);

        }
    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 + "获得口红的锁");
    }
    }

}

}
死锁避免的条件

1、互斥条件:一个资源每次只能被一个进程使用

2、请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放

3、不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺

4、循环等待条件,若干进程之间形成一种头尾相接的循环等待资源关系

13、Lock锁

通过显示定义同步锁对象来实现同步,同步锁使用Lock对象充当

java.util,concurrent.locks.Look接口时控制多个线程对共享资源进行访问的工具,锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得LO=ock对象

ReentrantLock(可重入锁)类实现了 Lock,他拥有与synchronized相同的并发性和内存定义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显示加锁,释放锁

package Thread;

import jdk.nashorn.internal.ir.Block;

import java.util.concurrent.locks.ReentrantLock;

public class TestLock {
public static void main(String[] args) {
TestLock2 testLock2 = new TestLock2();
new Thread(testLock2).start();
new Thread(testLock2).start();
new Thread(testLock2).start();
new Thread(testLock2).start();
}
}
class TestLock2 implements Runnable{
int ticketNums = 10;

private final ReentrantLock Lock = new ReentrantLock();

@Override
public void run() {
    while (true){
        Lock.lock();
        try {

            if (ticketNums >0){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(ticketNums--);
            }else {
                break;
            }
        }finally {
            Lock.unlock();
        }
    }
}

}
synchronized与Lock的对比

Lock是显示锁(手动开启和关闭锁,别忘记关闭锁) syncharized是隐式锁,出了作用域自动释放

Lock只有代码块锁,syncharized有代码块锁和方法锁

使用Lock锁,jvm将花费较少的时间来调度线程,性能更好,并且具有更好的扩展性(提供更多的子类)

优先使用顺序:

Lock > 同步代码锁(已经进入了方法体,分配了相应资源)> 同步方法(在方法体之外)

14、线程协作

wait 方法:表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁

notify方法:唤醒一个处于等待状态的线程

管程法

package Thread;

import java.util.concurrent.Callable;

public class TestPC {
public static void main(String[] args) {
SynContainer synContainer = new SynContainer();
new Productor(synContainer).start();
new Consumer(synContainer).start();

}

}
class Productor extends Thread{
SynContainer container;
public Productor(SynContainer container){
this.container = container;
}

@Override
public void run() {
    for (int i = 0; i < 100; i++) {
        container.push(new Chicken(i));
        System.out.println("生产了" + i+"只鸡");
    }
}

}
class Consumer extends Thread{
SynContainer container;
public Consumer(SynContainer container){
this.container=container;
}

@Override
public void run() {
    for (int i = 0; i < 100; i++) {
        System.out.println("消费了--》"+container.pop().id+"只鸡");
    }
}

}
class Chicken{
int id;

public Chicken(int id) {
    this.id = id;
}

}

class SynContainer {
Chicken[] chickens = new Chicken[10];
int count = 0;

public synchronized void push(Chicken chicken)  {
    if (count == chickens.length){
        try {
            this.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    chickens[count] = chicken;
    count++;
    this.notifyAll();

}
public synchronized Chicken pop(){
    if (count == 0){
        try {
            this.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    count--;
    Chicken chicken = chickens[count];
    this.notifyAll();
    return chicken;
}

}
信号灯法

package Thread;

//测试生产者消费者问题2,信号灯法,标识位解决
public class TestPC2 {
public static void main(String[] args) {
TV tv = new TV();
new Player(tv).start();
new Watcher(tv).start();
}

}
//生产者-演员
class Player extends Thread{
TV tv;
public Player(TV tv){
this.tv = tv;
}

@Override
public void run() {
    for (int i = 0; i < 20; i++) {
        if (i%2 ==0){
            this.tv.Play("正在表演快乐大本营");
        }else {
            System.out.println("抖音");
        }
    }
}

}

//消费者--观众
class Watcher extends Thread {

TV tv;
public Watcher(TV tv){
    this.tv = tv;
}

@Override
public void run() {
    for (int i = 0; i < 20; i++) {
        tv.Watch();
    }
}

}
//产品---节目
class TV{
//演员表演,观众等待

//观众观看,演员等待
String voice;
boolean flag = true;

//表演
public synchronized void Play(String voice){
    if (!flag){
        try {
            this.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    System.out.println("演员表演了:"+ voice);
    //通知观众观看
    this.voice= voice;
    this.flag = !this.flag;
    this.notify();
}
//观看
public synchronized void Watch(){
    if (flag){
        try {
            this.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    System.out.println("观看了:"+voice);
    this.notify();
    this.flag = !this.flag;
}

}
15、线程池

背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。

思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁的创建销毁、实现重复利用,类似生活中的公共交通工具

好处:提高影响速度(减少了创建新线程的时间)

降低资源消耗(重复利用线程池中线程,不需要每次都创建)

标语线程管理(...)

corePoolSize:核心池的大小

maximumPoolSize:最大线程数

keepAlive Time :线程没有任务时最多保持多长时间后会终止

jdk5.0起提供了线程池相关的API:ExecutorService和Executors

ExecutorService:真正的线程池接口,常见字类ThreadPoolExecutor

void execute(Runnable command): 执行任务/命令,没有返回值,一般执行Runnable

Futruesubmit(Callabletask):执行任务,又返回值,一般涌来执行Callable

void shutdowr():关闭连接池

Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池

总结

package Thread;

import java.util.concurrent.Callable;

public class ThreadNew {
public static void main(String[] args) {
new MyThread1().start();
new Thread(new MyThread2()).start();

}

}
//1、继承Thread类
class MyThread1 extends Thread{
@Override
public void run() {

}

}
//2、实现Runnable类
class MyThread2 implements Runnable{
@Override
public void run() {

}

}

//3、实现Callable接口
class MyThread3 implements Callable{
@Override
public Integer call() throws Exception {
System.out.println(1);
return 100;
}
}

posted @ 2021-05-17 14:46  期待aaa  阅读(69)  评论(0)    收藏  举报