Java多线程实现以及线程安全笔记

Java虚拟机允许应用程序并发地运行多个线程。

以下为多线程的实现常用的2种方法

(1)继承Thread类,重写run()方法

     Thread本质上也是实现了Runnable接口的一个实例,代表一个线程的实例。启用线程的唯一方法就是通过Thread类的start()方法。调用start()方法后并不是立即执行多线程代码,而是使得该线程变为可运行态(Running),什么时候运行是由操作系统决定的。

class MyThread extends Thread{//创建函数体
    public void run(){
        System.out.println("Thread body");//线程函数体
    }
}
public class Test{
    public static void main(String[] args){
        MyThread thread = new MyThread();
        thread.start();//线程开启 
    }
}

(2)实现Runnable接口,并实现该接口的run()方法

主要步骤

  • 自定义类并实现Runable接口,实现run()方法。
  • 创建Thread对象,用实现Runnable接口的对象作为参数实例化该Thread对象
  • 调用Thread的start()方法
class MyThread implements Runnable{
    public void run(){
        System.out.println("Thread body");
    }
}
public class Test{
    public static void main(String []args){
        MyThread thread = new MyThread();
        Thread t = new Thread(thread);
        t.start();//开启线程
    }
}

这两种方法最终都是通过Thread 的对象的API来控制线程的。

 

线程状态: 

  • NEW  新建状态

  • RUNNABLE  运行状态

  • BLOCK 受阻塞   线程具有CPU的执行资格

  • WAIT (无限期)等待  object的方法   notify(唤醒)  放弃CPU

  • TIMED_WAITING 休眠  sleep   放弃CPU

  • TERMINATED 死亡

 

线程池

  • 节约开销

  • 由线程池工厂创建的,再调用线程池中的方法获取线程,再通过线程去执行方法

  • 在java.util.concurrent 中的Executors 类

    • 实现线程池程序,使用供现场类Executors中的静态方法创建线程对象,指定线程个数

      static ExectorService newFixedThreadPool(int 个数) 返回线程池对象

      返回的是ExecutorService接口的实现类(线程池对象)

      接口实现类对象,调用方法submit (Runnable r) 提交线程执行任务

  • run方法没有返回值,不能抛异常

 

线程开启虚拟栈空间,栈内存是线程私有的

创建线程的目的:为了建立程序单独的执行路径

Thread.currentThread();返回正在执行的线程对象

 

线程安全

  • 单线程没有安全问题

  • 多线程同时操作同一个共享数据,往往出现安全问题

    • 解决:当一个线程进入数据操作的时候,无论是否休眠,其他线程只能等待。保证只有一个线程在操作

      • 同步代码块:

        • synchronized(任意对象){ 线程要操作的共享数据 }

        • 同步对象,任意对象。对象:同步锁,对象监视器 obj

        • 同步保证安全性:没有锁的线程不能执行,只能等。加了同步后,线程进同步判断锁,获取锁,出同步释放锁,导致程序运行速度的下降。 

        • 线程遇到同步代码块后,线程判断同步锁还有没有。

          • 如果有,获取锁,进入同步中,去执行,执行完毕后将锁对象还回去。在同步中线程,进行了休眠,此时另一个线程,会执行。

          • 没有锁的线程,不能进入同步中执行,被阻挡在同步代码块的外面。在同步中的线程,不出去同步,不会释放锁。

      • 同步方法:代码简洁,将线程共享数据,和同步,抽取到一个方法中。对象锁是本类对象引用this.如果方法为静态,锁是本类自己.class 属性

电影院卖票,排队上厕所原理

package ticket;
/**
* 多线程并发访问同一个数据资源
* 3 个线程,对一个票资源,出售
*/
public class ThreadDemo {
    public static void main(String[] args) {
    //创建Runnable接口实现类对象
    Tickets t = new Tickets();
    //创建3个Thread对象,传递Runnable接口实现类
    Thread t0 = new Thread(t);
    Thread t1 = new Thread(t);
    Thread t2 = new Thread(t);
    t0.start();
    t1.start();
    t2.start();
    }
}

package ticket;
public class Tickets implements Runnable{
    private int ticket = 100;
    //同步代码块
//     private Object obj = new Object();
//     @Override
//     public void run() {
//             while (true){
//             //对票数判断,大于0,可以出售
//             synchronized (obj){//不写匿名对象是因为每次循环时,对象就变了
//                 if(ticket > 0){
//                     try {
//                         Thread.sleep(10);
//                     } catch (InterruptedException e) {
//                     }
//                 System.out.println(Thread.currentThread().getName()+"出售第"+ticket--);
//                }
//             }
//         }
//     }
//同步方法
    @Override
    public void run() {
        while (true){
            payTicket();
        }
    }
    //对票数判断,大于0,可以出售
    public synchronized void payTicket(){
        if(ticket > 0){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
            }
            System.out.println(Thread.currentThread().getName()+"出售第"+ticket--);
        }
    }
}

Lock

  • 可以替代synchronized,比他更灵活

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Tickets implements Runnable{
    private int ticket = 100;
    //在类的成员位置,创建Lock接口实现类的对象
    private Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true){    
            lock.lock();
            //对票数判断,大于0,可以出售
            if(ticket > 0){
                try {
                    Thread.sleep(10);
                    System.out.println(Thread.currentThread().getName()+"出售第"+ticket--);
                } catch (InterruptedException e) {
                }finally {
                    lock.unlock();
}}}}}

死锁

  • 同步锁使用的弊端:当线程任务中出现了多个同步(多个锁)时,如果同步中嵌套了其他的同步。这是容易引发一种现象:程序出现无限等待,这种现象称为死锁。

synchronized(A 锁){
    synchronized(B 锁){
    }
}
  • 前提:必须是多线程的,出现同步嵌套

  • 线程进入同步获取锁,不出去同步,不会释放锁

public class Main {
    public static void main(String[] args) {
        DeadLock deadLock = new DeadLock();
        Thread t0 = new Thread(deadLock);
        Thread t1 = new Thread(deadLock);
        t0.start();
        t1.start();
    }
}

public class DeadLock implements Runnable{
    private int i = 0;
    public void run(){
        while(true){
            if(i%2 == 0){
                //先进入A同步,再进入B同步
                synchronized (LockA.lockA){
                    System.out.println("if 。。。locka");
                    synchronized (LockB.lockB){
                        System.out.println("if。。。lockb");
                    }
                }
            }else{
                //先进入B同步,再进入A同步
                synchronized (LockB.lockB){
                    System.out.println("else。。lockb");
                    synchronized (LockA.lockA) {
                        System.out.println("else。。locka");
                    }
                }
            }
            i++;
        }
    }
}
public class LockA {
    private LockA(){}
    public static final LockA lockA = new LockA();
}
public class LockB {
    private LockB(){}
    public static final LockB lockB = new LockB();
}

等待唤醒机制

  • 线程之间通信:多个线程在处理同一个资源,单只处理的动作(线程任务)却不相同。通过一定的手段使各个线程能有效的利用资源。这种手段即——等待唤醒机制。

  • wait() 无限等

  • notify() 唤醒,一次只唤醒一个,任意的

  • notifyAll() 唤醒全部

 //保证两个线程交替输入输出值
public class ThreadDemo {
    public static void main(String[] args) {
        Resource r = new Resource();
        Input in = new Input(r);
        Output out = new Output(r);
        Thread tin = new Thread(in);
        Thread tout = new Thread(out);
        tin.start();
        tout.start();
    }
}
public class Resource {
    public String name;
    public String sex;
    public boolean flag = false;
}
public class Input implements Runnable {
    private Resource resource ;
    public Input(Resource r){
        resource = r;
    }
    //flag 为ture表示赋值完成,false为取值完成
    @Override
    public void run() {
        int i = 0;
        while(true){
            synchronized (resource){
                //标记是true,等待
                if(resource.flag){
                    try{resource.wait();}catch (Exception e){}
                }
                if(i%2 ==0){
                    resource.name = "张";
                    resource.sex = "男";
                }else{
                    resource.name = "li";
                    resource.sex = "nv";
                }
                //将对方线程唤醒,标记该为true
                resource.flag = true;
                resource.notify();
            }
            i++;
        }
    }
}

public class Output implements Runnable {
    private Resource r ;//new对象之后输出对象默认值,因为main方法中的对象和这对象不是一个。
    public Output (Resource r){
        this.r = r;
    }
    @Override
    public void run() {
        while(true){
            synchronized (r){
                // 判断标记,是false,等待
                if(!r.flag){
                    try {
                        r.wait();
                    }catch (Exception e){}
                }
                System.out.println(r.name+".."+r.sex);
                //标记改成false,唤醒对方线程
                r.flag = false;
                r.notify();
            }
        }
    }
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值