线程2

复习

1.线程的三种实现方式
继承线程类java.lang.Thread,重写run方法。在run方法中定义线程的任务。
实现java.lang.Runnable接口,实现run方法,在run 方法中定义任务。
实现Callable 接口。实现call 方法,在call 方法中定义任务。

2 .线程的生命周期
新建 调用 start方法,进入 就绪(可以运行)状态,等待cpu调度执行。当cpu调度执行 进入 运行状态 ,当cpu时间片结束,从运行状态回到就绪状态。在运行状态中,当被阻塞的时候,进入阻塞状态,解除阻塞,进入就绪状态。如果调用了stop方法,或者被中断了,或者run方法结束,那么线程进入死亡状态。

3 方法
优先级,范围1-10
Join() 导致当前线程被阻塞的。
Thread.yield 导致当前线程从运行到就绪。
Thread.sleep 导致当前线程从运行到阻塞,超过指定是时间,解除。
守护线程:如果一个进程中所有的活动的线程都是守护线程,那么jvm杀死该进程。
线程中断。

4 线程安全
只有多线程的情况下,多个线程同时访问同一个数据,才存在线程安全问题。
解决的方法:同步代码块,同步方法,Lock 锁。

第一节监视器对象的选择、Lock

1. 监视器对象选择

如果想实现线程之间的互斥访问,那么监视器对象的选择,必须是唯一的,而且是不可改变的。
当一个类被加载的时候,就会产生一个对象(在堆中),该对象唯一,而且不可变。类型是Class 大class类型。Class 类是描述类的类。该对象通常是作为监视器对象使用的。可以通过 类名.class 来访问该对象。
说明:
(为什么是不可变的呢,假如有一个监视器对象是Object o,有一个线程将它锁住了,但是在线程内部又将这个引用指向了其他对象,那么这个时候,其他线程在进来的时候就可以进来了,所以得是唯一的。)(监视器对象可以是this、也可以是static final Object o 但是还得单独创建一个对象,那么有没有一种东西,已经存在了,而且是唯一的不可变的?有。例如:在下面的代码中的Account 在类加载的时候就会产生一个对象,在类加载的时候其实产生 两部分内存,一部分是类的字节码的源数据会加载到方法区还会产生一个对象,这个涉及到我们后面的内容反射了,这个对象在堆中,类型是Class,Class是描述类的类。如何得到这个Class类的对象呢?类名.class 例如:Account.class。所以以后这个同步代码块在哪个类中用,就用那个类的大Class就行了。)

例子:

synchronized (Account.class) {}

同步方法:本质上也是使用监视器对象实现线程互斥访问。监视器对象是隐式指定的。
同步的实例方法,它的隐式的监视器对象是:this
同步的静态方法,它的隐式的监视器对象是:当前类的 Class 对象。

2. Lock 锁实现线程的互斥访问

说明:Lock 锁是在jdk1.5之后推出的。是针对高并发,还要追求安全,提出相关的线程同步的内容。Lock 是一个接口。用的是它的实现的子类。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 学习锁
 */
public class TestLock {
    public static void main(String[] args) {
        AccountRunnable accountRunnable0=new AccountRunnable();
        Thread thread1=new Thread(accountRunnable0,"张三");
        Thread thread2=new Thread(accountRunnable0,"张三媳妇");

        thread1.start();
        thread2.start();


    }
}
//账户类
class Account{
    //账户余额
    private int money=1500;
    //创建一个可重入锁对象
    //传入true代表是一个公平锁,等待请求锁资源线程时间最长的线程
    //会被优先获得锁资源,效率会受到一定的影响。
    private Lock lock=new ReentrantLock();
    //取钱的方法,返回是否取钱成功
    public boolean withDraw(int money){
        //效率比同步代码块和同步方法效率都高
       lock.lock();
        try {
            if(this.money>=money){
                //问题扩大
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.money-=money;
                System.out.println(Thread.currentThread().getName()+"取钱成功!余额还剩:"+this.money);
                return true;
            }
        } finally {
            lock.unlock();//是必须要执行的

        }

        System.out.println(Thread.currentThread().getName()+"取钱失败!余额还剩:"+this.money);
        return false;
    }
}
//任务类,去同一个账户取钱
class AccountRunnable implements  Runnable{
    Account account=new Account();
    @Override
    public void run() {
        account.withDraw(1000);
    }
}

3 .死锁

**
 * 死锁的学习
 */
public class TestDeadLock {
    public static void main(String[] args) {
        DeadLockThread thread0=new DeadLockThread(0);
        DeadLockThread thread1=new DeadLockThread(1);

        thread0.start();
        thread1.start();
    }

}

class DeadLockThread extends Thread{
    private int id;
    public static final Object o1=new Object();
    public static final Object o2=new Object();

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

    @Override
    public void run() {
        if(id==0){
            //线程-0访问的代码
            synchronized (o1){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(getName()+"--o1--o2--");
                ///stop here  线程-1 锁住了o1请求,锁o2
                synchronized (o2){
                    System.out.println(getName()+"--o2--o1--");
                }
            }
        }else{
            //线程-1访问的代码
            synchronized (o2){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(getName()+"-o2-o1-");
                //stop here  线程-2 锁住了o2请求,锁o1
                synchronized (o1){
                    System.out.println(getName()+"-o1-o2-");
                }
            }

        }
    }
}

第二节 生产者消费者问题

/**
 *测试类 
 */
public class Test {
    public static void main(String[] args) {
        MyStack<Steam> myStack=new MyStack<>();
        Consumer consumer=new Consumer(myStack);
        Producer producer=new Producer(myStack);

        consumer.start();
        producer.start();
    }
}



import javax.xml.bind.annotation.XmlType;
/**
 *wait():第一:让当前线程在当前对象上等待。第二:并对当前对象解锁。
 * notify():唤醒当前对象上的某个线程
 */
public class MyStack<E> {
    //定义栈的初始容量
    private static final int DEFAULT_CAPACITY=3;
    //底层使用数组实现
    private Object[] elementDate;
    //栈顶指针
    private int index;
    //构造方法
    public MyStack() {
        elementDate=new Object[DEFAULT_CAPACITY];
    }
    //压栈操作 生产者线程 需要执行的内容
    public synchronized void push(E o){
        if(isFull()){
            //栈满了
            try {
                //让生产者线程在this上等待,从运行状态进入阻塞状态
                //并释放this上的锁,对this解锁
                //当前线程必须对this上锁(当前线程必须拥有此对象监视器)
                //监视器对象必须是谁调用了wait谁必须是那个监视器对象。
                System.out.println("生产者等待了");
                this.wait();//让生产者线程在容器对象上等待。谁调用wait()谁是当前对象,谁执行this.wait()这一行代码,谁是当前线程。
                System.out.println("生产者被唤醒了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //生产者将商品放入容器的过程
        elementDate[index++]=o;
        System.out.println(Thread.currentThread()+"生产了"+o);
        //通知消费者可以继续消费了。
        this.notify();
    }
    
    //消费者线程执行的内容
    public synchronized E pop(){
        //栈空了
        if(isEmpty()){
            try {
                //让消费者线程,在this上等待
                //消费者线程从运行状态进入阻塞状态,对this解锁
                System.out.println("消费者等待了");
                this.wait();
                System.out.println("消费者被唤醒了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //指针下移,将指针走过的数据下移
        E o=(E)elementDate[--index];
        //避免内存泄漏
        elementDate[index]=null;
        System.out.println(Thread.currentThread().getName()+"消费了"+o);
        //唤醒在this上等待的某一个线程(生产者),从阻塞状态进入到就绪状态。
        //消费者消费了一个商品,可以通知生产者继续生产。
        this.notify();
        return o;
    }

    //判断栈是否满了
    boolean isFull(){
        return index==elementDate.length;
    }
    //判断栈是否空l
    boolean isEmpty(){
        return index==0;
    }
    
}


**
 *生产者
 */
public class Producer extends Thread {
    private MyStack<Steam> stack;
    public Producer(MyStack<Steam> stack) {
        this.stack = stack;
    }

    @Override
    public void run() {
        for (int i = 0; i < 6; i++) {
            stack.push(new Steam(i));
        }
    }
}



/**
 * 消费者
 */
public class Consumer extends Thread{
    private MyStack<Steam> stack;

    public Consumer(MyStack<Steam> stack) {
        this.stack = stack;
    }
    @Override
    public void run() {
        for (int i = 0; i < 6; i++) {
            stack.pop();
        }
    }
}


/**
 *商品类
 */
public class Steam {
    private int i;

    public Steam(int i) {
        this.i = i;
    }

    @Override
    public String toString() {
        return "馒头{" +
                "i=" + i +
                '}';
    }
}
//如果同步代码中用MyStack.class作为监视器的话,那么在调用wait()方法的时候也必须用MyStack.class
 //压栈操作 生产者线程 需要执行的内容
    public  void push(E o){
        synchronized (MyStack.class){
            if(isFull()){
                //栈满了
                try {
                    //让生产者线程在this上等待,从运行状态进入阻塞状态
                    //并释放this上的锁,对this解锁
                    //当前线程必须对this上锁
                    System.out.println("生产者等待了");
                    MyStack.class.wait();
                    System.out.println("生产者被唤醒了");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //生产者将商品放入容器的过程
            elementDate[index++]=o;
        }
        System.out.println(Thread.currentThread()+"生产了"+o);
        //通知消费者可以继续消费了。
        this.notify();
    }

3 jdk1.5针对死锁问题的解决方案

//这里只是部分代码块,不可运行
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 *多个消费者生产者导致的死锁问题解决方案(dk1.5针对死锁问题的解决方案)
 */
public class MyStack<E> {
    //定义栈的初始容量
    private static final int DEFAULT_CAPACITY=3;
    //底层使用数组实现
    private Object[] elementDate;
    //栈顶指针
    private int index;
    Lock lock =new ReentrantLock();
    //生产者监视器
    Condition proCon=lock.newCondition();
    //生产者监视器
    Condition conCon=lock.newCondition();

    //构造方法
    public MyStack() {
        elementDate=new Object[DEFAULT_CAPACITY];
    }
    //压栈操作 生产者线程 需要执行的内容
    public void push(E o){
        lock.lock();
        try {
            while(isFull()){
                try {
                    System.out.println(Thread.currentThread().getName()+"等待了");
                    proCon.await();
                    System.out.println(Thread.currentThread().getName()+"换醒了");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            elementDate[index++]=o;
            System.out.println(Thread.currentThread()+"生产了"+o);
            conCon.signal();
        } finally {
            lock.unlock();
        }
    }

    //消费者线程执行的内容
    public E pop(){
        lock.lock();
        try {
            while(isEmpty()){
                try {
                    System.out.println(Thread.currentThread().getName()+"等待了");
                    conCon.await();
                    System.out.println(Thread.currentThread().getName()+"唤醒了");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            E o=(E)elementDate[--index];
            elementDate[index]=null;
            System.out.println(Thread.currentThread().getName()+"消费了"+o);
            proCon.signal();
            return o;
        } finally {
            lock.unlock();
        }
    }

    //判断栈是否满了
    boolean isFull(){
        return index==elementDate.length;
    }
    //判断栈是否空l
    boolean isEmpty(){
        return index==0;
    }
}

第三节 线程池

Thread pool 只需要用户给线程池对象提供任务即可。

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * 线程池
 */
public class TestPool {
    public static void main(String[] args) throws Exception{
        //test();
        test1();
    }
    //使用线程池技术处理多个Runnable任务
    static void test(){
        //创建线程池对象
        //创建一个只有一个线程的线程池对象
        ExecutorService pool= Executors.newSingleThreadExecutor();
        //创建一个动态数量的线程池对象,根据任务的数量,动态创建和销毁
        ExecutorService pool1=Executors.newCachedThreadPool();
        //创建固定数量的线程的线程池
        ExecutorService pool2=Executors.newFixedThreadPool(10);
        //创建了20个任务
        for (int i = 0; i < 20; i++) {
            Runnable runnable=new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"\t开始执行任务");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"\t任务结束了");
                }
            };
            //让Runnable 任务给线程池去执行
             pool1.execute(runnable);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //关闭线程池,所有任务都结束之后,关闭
        pool1.shutdown();
    }

    //使用线程池执行大量的Callable任务
    static void test1() throws Exception{
        ExecutorService pool=Executors.newFixedThreadPool(10);
        //使用容器管理,线程执行的结果数据
        List<Future<Integer>> list=new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            //20个任务
            Callable<Integer> callable=new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    System.out.println(Thread.currentThread().getName()+"\t开始执行任务");
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName()+"\t任务结束了");
                    return (int)(Math.random()*100);
                }
            };
            //提交任务
            Future<Integer> future=pool.submit(callable);
            list.add(future);
        }
        //获得每一个线程的结果
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i).get());
        }
        //关闭
        pool.shutdown();

    }
}

第四节单例设计模式

1 .单例模式

需求:希望某一个类只有一个唯一的实例。Singleton。

/**
 *单例设计模式
 */
public class TestSingleton {
    public static void main(String[] args) {
        new Thread(){
            @Override
            public void run() {
                System.out.println(MySingleton2.getInstance());
            }
        }.start();
        new Thread(){
            @Override
            public void run() {
                System.out.println(MySingleton2.getInstance());
            }
        }.start();

    }
}
//设计一个单例类 饿汉模式
class MySingleton1{
    //instance指向唯一的实例
    //类加载的时候执行,只执行一次
    //恶汉模式:类加载的时候就创建了唯一的对象,对系统的开销相对较大
    //可能会产生影响软件启动的延时问题
    private final static MySingleton1 instance =new MySingleton1();

   //私有化构造方法
    private  MySingleton1() {
    }

    //必须对外提供一个可以访问唯一实例的方法,并返回唯一实例
    public static MySingleton1 getInstance(){
        return instance;
    }
}


//设计一个单例类  懒汉
class MySingleton2{
    //instance指向唯一的实例
    private static MySingleton2 instance;
    //私有化构造方法
    private MySingleton2(){}

    //必须对外提供一个可以访问唯一实例的方法。并返回该唯一实例
    //懒汉模式,就是在第一次调用getInstance()方法的时候创建唯一的实例
    public static MySingleton2 getInstance(){
        //提高效率,避免每次都进行同步监视器锁的判断
        if(instance==null){
            // 2stop
            synchronized (MySingleton2.class){
                // 1stop
                if(instance==null){
                    // stop here
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    instance=new MySingleton2();
                }
            }
        }
        return instance;
    }

}

**
 * 一道面试题
 */
public class Test9 {
    public static void main(String[] args) {
        Runnable runnable=new Runnable() {
            @Override
            public void run() {
                System.out.println("222");
            }
        };
        //new Thread(runnable).start();
        new Thread(){  //这里是父类引用指向子类对象,重写了run方法  所以执行的是子类中的run 这是多态
            @Override
            public void run() {
                //super.run();
                System.out.println("111");
            }
        }.start();
    }
}

问题:new Thread(runnable).start() 为什么把runnable传进去之后就 执行的是Runnable里面的run方法呢?看源码:

//先看Thread类的源码:
public
class Thread implements Runnable {
}
----------------------------------------------------------------------------------------------------------
  /* What will be run. */
    private Runnable target;  //有一个target属性
-----------------------------------------------------------------------------------------------------------
 public Thread(Runnable target) {  //需要一个Runnable类型的实参出入
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
-----------------------------------------------------------------------------------------------------------
//再点init()方法往里看
  private void init(ThreadGroup g, Runnable target, String name,   //需要一个Runnable类型的实参传入
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;

        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
            /* Determine if it's an applet or not */

            /* If there is a security manager, ask the security manager
               what to do. */
            if (security != null) {
                g = security.getThreadGroup();
            }

            /* If the security doesn't have a strong opinion of the matter
               use the parent thread group. */
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }

        /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
        g.checkAccess();

        /*
         * Do we have the required permissions?
         */
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }

        g.addUnstarted();

        this.group = g;
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        this.target = target;  ///就是这句代码导致的,把runnable传进去之后就 执行的是Runnable里面的run方法
        setPriority(priority);
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        /* Set thread ID */
        tid = nextThreadID();
    }

    /**
     * Throws CloneNotSupportedException as a Thread can not be meaningfully
     * cloned. Construct a new Thread instead.
     *
     * @throws  CloneNotSupportedException
     *          always
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }
-----------------------------------------------------------------------------------------------------------

每日一练:

0:模拟十个人穿越山洞,只能一个人一个人的通过山洞,打印每个人穿越山洞的次序.
使用线程实现,穿越是线程互斥的.

1:使用wait notify notifyAll 实现功能.
一个线程打印 数字 一个线程打印 A-Z
打印的结果:12A34B56C…

import java.util.concurrent.Callable;

/**
 *1.模拟十个人穿越山洞,只能一个人一个人的通过山洞,打印每个人穿越山洞的次序.
 *使用线程实现,穿越是线程互斥的.
 */
public class MulThreadTest{
    public static void main(String[] args) {
        //创建一个山洞(0.5分)
        Tunnel tul = new Tunnel();
        //创建十个过山洞线程(1.0分)
        Thread p1 = new Thread(tul,"p1");
        Thread p2 = new Thread(tul,"p2");
        Thread p3 = new Thread(tul,"p3");
        Thread p4 = new Thread(tul,"p4");
        Thread p5 = new Thread(tul,"p5");
        Thread p6 = new Thread(tul,"p6");
        Thread p7 = new Thread(tul,"p7");
        Thread p8 = new Thread(tul,"p8");
        Thread p9 = new Thread(tul,"p9");
        Thread p10 = new Thread(tul,"p10");
        //启动十个线程0.5分
        p1.start();        p2.start();
        p3.start();        p4.start();
        p5.start();        p6.start();
        p7.start();        p8.start();
        p9.start();        p10.start();
    }
}
class Tunnel implements Runnable{
    private int crossedNum = 0;//初始人数0.5分
    public void run(){//1分
        cross();
    }
    public  synchronized void cross(){
        try {  	//每个人通过山洞的时间为5秒(1分)
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
//人数计数(0.5分)
        crossedNum++;
        //显示一下每次通过山洞人的姓名(1分)
        System.out.println(Thread.currentThread().getName()+
                "通过了山洞,这是第"+crossedNum+"个用户");
    }
}

/**这个版本程序没停,有bug,抽时间改下
 *使用wait notify notifyAll 实现功能.
 *一个线程打印 数字  一个线程打印 A-Z
 *打印的结果:12A34B56C78.....
 */
public class TestPrint {
    public static void main(String[] args) {
        Print print=new Print();
        PrintLetter printLetter=new PrintLetter(print);
        printLetter.setPriority(Thread.MIN_PRIORITY);
        PrintNum printNum=new PrintNum(print);
        printNum.setPriority(Thread.MAX_PRIORITY);


        printNum.start();
        printLetter.start();
    }
}

/**
 * 使用wait notify notifyAll 实现功能.
 */
public class Print {
    private int count=0;
    //打印字母
    synchronized void letter(){
        for (char i = 'A'; i <  'Z' +1 ; i++) {
            if(count%2==0){
                System.out.println(i);
                this.notify();
            }
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }


        }
    }

    //打印数字
    synchronized void num(){
            for (int i = 1;  ; i++) {
                System.out.println(i);
                count++;
                if(count%2==0){
                    try {
                        this.notify();
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
    }
}

/**
 * 打印字母A-Z
 */
public class PrintLetter extends Thread{
    private Print print;

    public PrintLetter(Print print) {
        this.print = print;
    }

    @Override
    public void run() {
        print.letter();
    }
}

import com.bjsxt.first.Producer;

/**
 * 打印数字0-9
 */
public class PrintNum extends Thread{
    private Print print;

    public PrintNum(Print print) {
        this.print = print;
    }

    @Override
    public void run() {
       print.num();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值