首页 > 编程语言 > 详细

Java多线程详解

时间:2021-05-26 22:06:59      阅读:29      评论:0      收藏:0      [点我收藏+]

线程简介

??进程是执行程序的一次执行过程,它是一个动态的概念,是系统资源分配的单位。通常一个进程中可以包含若干个线程,线程是CPU调度和执行的单位。

注意:
??很多多线程是模拟出来的,真正的多线程是指有多个CPU,即多核。如果是模拟出来的多线程,即在一个CPU的情况下,在同一个时间点,CPU只能执行一个代码,因为切换的很快,所以就有同时执行的错觉。

线程的创建

继承Thread类

实现Runnable接口

实现Callable接口

继承Thread类

1. 自定义线程类自定义Thread类
2. 重写run()方法,编写线程执行体
3. 创建线程对象,调用start()方法启动线程

example:

public class ThreadTest extends Thread{
    public static void main(String[] args) {
        ThreadTest test = new ThreadTest();
        test.start();
        for (int i = 0; i < 5; i++) {
            System.out.println("这是主线程"+i);
        }
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            // TODO Auto-generated method stub
            System.out.println("这是分线程" + i);
        }
    }
}

result:

这是主线程0
这是主线程1
这是分线程0
这是主线程2
这是分线程1
这是分线程2
这是主线程3
这是分线程3
这是主线程4
这是分线程4

线程开启不一定立即执行,有CPU调度执行。

实现Runnale接口

1. 自定义类实现Runnable接口
2. 实现run()方法,编写线程执行体
3. 创建线程对象,调用start()方法启动线程

example:

public class RunnableTest implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("这是分线程" + i);
        }
    }

    public static void main(String[] args) {
        RunnableTest test = new RunnableTest();
        Thread thread = new Thread(test);
        thread.start();

        for (int i = 0; i < 5; i++) {
            System.out.println("这是主线程" + i);
        }
    }
}

result:

这是主线程0
这是分线程0
这是分线程1
这是主线程1
这是分线程2
这是分线程3
这是主线程2
这是主线程3
这是分线程4
这是主线程4

Thread类实现Runnable接口

实现Callable接口


1. 自定义类实现Callable,需要返回值类型
2. 重写call()方法,需要抛出异常
3. 创建目标对象
4. 创建执行服务
5. 提交执行
6. 获取结果
7. 关闭服务

example:

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class CallableTest implements Callable<Boolean>{
    private static int ticketNum = 10;
    private String name;

    public CallableTest(){
        super();
    }

    public CallableTest(String name){
        this.name = name;
    }

    @Override
    public Boolean call()  {
        while (true) {
            if(ticketNum <= 0){
                break;
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name + "->拿到了第" + ticketNum-- + "票");
        }
        return true;
    }

    public static void main(String[] args) {
        CallableTest test1 = new CallableTest("小明");
        CallableTest test2 = new CallableTest("小红");
        CallableTest test3 = new CallableTest("小花");

        ExecutorService service = Executors.newFixedThreadPool(3);  //创建执行服务

        Future<Boolean> r1 = service.submit(test1);                 //提交执行
        Future<Boolean> r2 = service.submit(test2);
        Future<Boolean> r3 = service.submit(test3);

        boolean result1;
        boolean result2;
        boolean result3;

        try{
            result1 = r1.get();                                     //获取结果
            result2 = r2.get();
            result3 = r3.get();
            System.out.println("result1 = " + result1);
            System.out.println("result2 = " + result2);
            System.out.println("result3 = " + result3);
        } catch (Exception e) {
            e.printStackTrace();
        } finally{
            service.shutdownNow();                                  //关闭服务
        } 
    }
}

result:

小红->拿到了第10票
小花->拿到了第8票
小明->拿到了第9票
小明->拿到了第7票
小红->拿到了第5票
小花->拿到了第6票
小明->拿到了第3票
小红->拿到了第2票
小花->拿到了第4票
小花->拿到了第1票
小明->拿到了第-1票
小红->拿到了第0票
result1 = true
result2 = true
result3 = true

多个线程操作同一个资源的情况下,存在线程不安全问题。

线程的五大状态

创建状态:

??Thread thread = new Thread();

??线程对象一旦创建就进入到新生状态

线程的五大状态

创建状态:

?Thread thread = new Thread();

?线程对象一旦创建就进入到新生状态

就绪状态

??当调用start()方法,线程立即进入就绪状态,但不意味着立即调度执行。

运行状态

??进入运行状态,线程才真正执行线程体的代码块。

阻塞状态

??当调用sleep(),wait()或同步锁时,线程进入阻塞状态,就是代码不往下执行,阻塞事件解除后,重新进入就绪状态,等待CPU调度执行。

死亡状态

??线程中断或者结束,一旦进入死亡状态,就不能再次启动。

线程常用的方法:

1. setPriority(int newPriority)         更改线程的优先级
2. static void sleep(long millis)       在指定的毫秒数内让正在执行的线程对象休眠
3. void join()                          等待该线程终止
4. static void yield()                  暂停当前正在执行的线程对象,并执行其它线程
5. void interrupt()                     中断线程
6. boolean isAlive()                    测试线程是否处于活动状态

线程的同步机制

同步方法和同步块

同步方法锁的是this

同步方法:

public class TicketTest implements Runnable{
    private int ticketNum = 10;
    boolean flag = true;

    @Override
    public void run() {
        while (flag) {
            try {
                buy();
            } catch (Exception e) {
                e.printStackTrace();
            }        
        }
    }

    public synchronized void buy() throws InterruptedException{
        if(ticketNum <= 0){
            flag = false;
            return;
        }
        Thread.sleep(100);
        System.out.println(Thread.currentThread().getName() + "->拿到了第" + ticketNum-- + "票");
    }
    public static void main(String[] args) {
        TicketTest ticket = new TicketTest();

        new Thread(ticket,"小明").start();
        new Thread(ticket,"老师").start();
        new Thread(ticket,"黄牛党").start();
    }
}

result:

小明->拿到了第10票
老师->拿到了第9票
老师->拿到了第8票
老师->拿到了第7票
老师->拿到了第6票
老师->拿到了第5票
黄牛党->拿到了第4票
黄牛党->拿到了第3票
黄牛党->拿到了第2票
黄牛党->拿到了第1票

同步块

public class Bank {
    public static void main(String[] args) {
        Account account = new Account(100, "零钱");
        Drawing boyFriend = new Drawing(account, 50, "boyFriend");
        Drawing grilFriend = new Drawing(account, 100, "grilFriend");

        boyFriend.start();
        grilFriend.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);
        }
    }
}

result:

零钱余额为:50
boyFriend手里的钱:50
grilFriend钱不够了,取不了

锁的对象是变量的量,需要增删改的对象

死锁问题:

多个线程互相持有对方需要的资源,形成僵持。

public class DeadLock {
    public static void main(String[] args) {
        Makeup girl1 = new Makeup(0, "小娜");
        Makeup girl2 = new Makeup(1, "小樱");

        girl1.start();
        girl2.start();
    }
}

class Lipstick{

}

class Mirror{

}

class Makeup extends Thread{
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();
    int choice;
    String girlName;

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

result:

小娜获得口红的锁
小樱获得镜子的锁

解决死锁问题

        Makeup girl1 = new Makeup(0, "小娜");
        Makeup girl2 = new Makeup(1, "小樱");

        girl1.start();
        girl2.start();
    }
}

class Lipstick{

}

class Mirror{

}

class Makeup extends Thread{
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();
    int choice;
    String girlName;

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

result:

小樱获得镜子的锁
小娜获得口红的锁
小娜获得镜子的锁
小樱获得口红的锁

产生死锁的必要条件:

  1. 互斥条件:一个资源每次只能被一个进程使用
  2. 请求与保持:一个进程因请求资源而阻塞时,对已获得的资源保持不放
  3. 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺
  4. 循环等待条件:若干个进程之间形成一种头尾相接的循环等待资源关系

synchronized与Lock对比

  • Lock是显示锁(手动开启和关闭锁)synchronized是隐式锁,出了作用域自动释放
  • Lock只有代码块锁,synchronized有代码块锁和方法锁
  • 使用Lock锁,JVM将花费更少的时间来调度线程,性能更好并且具有更好的扩展性(提供更多的子类)
  • 优先使用顺序:Lock > 同步代码快 > 同步方法

Java多线程详解

原文:https://www.cnblogs.com/starsgrace/p/14814883.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!