java多线程的记录总结

线程的各种方法

  • 1.sleep():--睡眠--线程进入睡眠状态,不会释放锁(对于执行sleep方法的线程来说!)。
  • 2.yield():--释放--线程释放当前cpu,回到就绪状态,此时该线程和其他线程会同时来竞争cpu。这个方法不能保证,该线程释放后不再占有cpu,只是尽可能的让资源不要一直被该线程持续占用。
  • 3.wait():--等待--:进入线程等待池,进入挂起状态,释放锁。只能在同步控制方法或者同步控制块中使用。wait()是Object类的方法
  • 4.notify():--唤醒--只能在同步控制方法或者同步控制块中使用。由线程规划器随机挑选一个wait状态的线程,被唤醒!执行了该方法后不会立即释放wait的锁,而是等待notify这个方法执行完后才会释放锁。
  • 5.notifyAll(): --唤醒所有--使所有正在等待队列中等待听一个共享资源的全部线程从等待状态退出,进入可运行状态。
  • 6.interrupt:--唤醒--强制唤醒睡眠的线程。
  • 7.resume:--恢复--已经不用了
  • 8.suspend:--暂停--已经不用了
  • 9.stop:--销毁--已经不用了
  • 10.join:a线程在b线程的代码前使用了这个方法,那么要等a执行完后,才会继续执行b线程剩下的代码。

    

java多线程的创建

具体创建多线程。请看这个超链接!请点我这个超链接!

创建线程两种方式:继承Thread类,实现Runnable接口

注意:start()方法的调用后并不是立即执行多线程代码,而是使得该线程变为可运行态(Runnable),什么时候运行是由操作系统决定的。但是start方法重复调用的话,会出现java.lang.IllegalThreadStateException异常。

  • 实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。因此,不管是扩展Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的
  • 如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。

线程的状态

  • 1、新建状态(New):新创建了一个线程对象。
  • 2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
  • 3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
  • 4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
  • 5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

阻塞:

  • (一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
  • (二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
  • (三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)

volatitle关键字

    volatitle修饰的变量可以保证变量的其可见性。可见性是指当一条线程修改了这个变量的值,新值对于其他线程来说是立即可知的。

    jvm为了提高代码运行效率,不会每次去主内存取值,而是在自己的缓存立马存一份主内存的数据,然后修改值后,对主内存进行回写。如果一个线程改了一个变量,在这个线程把这个变量回写主内存前,另一个线程也访问了这个变量,就会造成变量不统一,结果就会混乱。而volatitle就规定线程每次访问变量时,会刷新主内存的变量,即要求其他的缓存数据立马回写,此时的变量就是修改后的变量。

注意:volatitle不能保证原子性。比如i++,看似只有一步其实是i=i+1;先读取i的值,然后+1,然后再把值回写到i,所以可能在读取值的时候,还没+1,别的线程也加载了,此时两者获得的都是同样的数据,就会造成少少+了一个1。

代码如下:

public class ThreadMain {
    public static void main(String[] args) {
        Thread threadA = new Thread(new ThreadDemo("A"));
        Thread threadB = new Thread(new ThreadDemo("B"));
        Thread threadC = new Thread(new ThreadDemo("C"));
        try {
            threadA.start();
            threadB.start();
            threadC.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public class ThreadDemo implements Runnable{
    private static volatile int i = 0;
    private String threadName;

    public ThreadDemo(String threadName) {
        this.threadName = threadName;
    }

    @Override
    public void run() {
        System.out.println("此时是线程:"+threadName);
        while(i<200000){
            i++;
            try {
                System.out.println(threadName+":i="+i);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
三个线程交替打印i,可能出现A、B或C线程打印出一样的i值。

Synchronized关键字

    这个关键字就保证了原子性
  • 只有共享的资源,读写的时候才需要同步化
  • 当一个线程执行的代码出现异常,会直接释放所有的锁。该线程停止,其他线程照常执行!。
  • “可重入锁” 自己可以可以再次获取自己的内部锁,比如有一条线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个 对象锁的时候还是可以获取的。
  • 在方法上加syncchronized和直接用syncchronized代码块相比,后者更快,只要把需要同步的逻辑锁住,其他逻辑不需要锁!执行更有效率!
  • 同步是无法继承的!

线程的通信

    等待/通知机制         

  • 每个锁对象都有两个队列,一个是就绪队列,一个是阻塞队列。
  • 当方法wait()执行后,锁被自动释放,但是执行完notify()方法后,不释放锁,要执行完notify()所在的同步sycchronized代码块后才释放锁!
  • 当线程呈wait状态时,调用线程对象的interropt()方法会出现InterruptedException异常
  • 代码如下:
    public class ThreadMain {
        public static void main(String[] args) {
            Thread threadWait = new Thread(new ThreadDemo("Wait"));
            Thread threadNotify = new Thread(new ThreadDemo("Notify"));
            try {
                threadWait.start();
                threadNotify.start();
                threadWait.wait();
                threadWait.interrupt();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
  • public class ThreadDemo implements Runnable{
        private String threadName;
    
        public ThreadDemo(String threadName) {
            this.threadName = threadName;
        }
    
        @Override
        public void run() {
            System.out.println("此时是线程:"+threadName);
        }
    }


    异常信息如下
java.lang.IllegalMonitorStateException
	at java.lang.Object.wait(Native Method)
	at java.lang.Object.wait(Object.java:503)
	at thread.ThreadMain.main(ThreadMain.java:16)

wait(long),等待一段时间内是否有线程对锁进行唤醒,如果超过这个时间自动被唤醒。 生产者\消费者模式
  • 生产者消费者共用同一把锁!
  • 消费者类:
  • public class Consumer {
        private String cValue;
    
        public Consumer(String cValue) {
            this.cValue = cValue;
        }
        void getValue(){
            try {
                synchronized (cValue){
                    if(PCValue.value.equals("")){
                       cValue.wait();
                    }
                    System.out.println("get:"+PCValue.value);
                    PCValue.value = "";
                    cValue.notify();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    生产者类:
    public class Producer {
        private String pValue;
    
        public Producer(String pValue) {
            this.pValue = pValue;
        }
    
        void setValue(){
            try {
                synchronized (pValue) {
                    if(!PCValue.value.equals("")){
                        pValue.wait();
                    }
                    PCValue.value = System.currentTimeMillis()+"_"+System.nanoTime();
                    System.out.println("set:" + PCValue.value);
                    pValue.notify();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    输出对象类:
    public class PCValue {
        public static String value = "";
    }
    消费者线程类
    public class ThreadConsumer implements Runnable{
        private String threadName;
        private Consumer consumer;
    
        public ThreadConsumer(String threadName, Consumer consumer) {
            this.threadName = threadName;
            this.consumer = consumer;
        }
    
        @Override
        public void run() {
            System.out.println("此时是线程:"+threadName);
            while(true){
                consumer.getValue();
            }
    
        }
    }
    生成者线程类:
    public class ThreadProducer implements Runnable{
        private String threadName;
        private Producer producer;
    
        public ThreadProducer(String threadName, Producer producer) {
            this.threadName = threadName;
            this.producer = producer;
        }
    
        @Override
        public void run() {
            System.out.println("此时是线程:"+threadName);
            while (true){
                producer.setValue();
            }
        }
    }
    主方法类:
    public class ThreadMain {
        public static void main(String[] args) {
            String lock = new String("");
            Producer producer = new Producer(lock);
            Consumer consumer = new Consumer(lock);
            Thread threadProducer = new Thread(new ThreadProducer("Producer",producer));
            Thread threadConsumer = new Thread(new ThreadConsumer("Consumer",consumer));
            threadConsumer.start();
            threadProducer.start();
        }
    }
    输出结果:
    set:1526353156432_235332618167724
    get:1526353156432_235332618167724
    set:1526353156432_235332618184682
    get:1526353156432_235332618184682
    set:1526353156432_235332618278394
    get:1526353156432_235332618278394
    保证每次消费后,就会生产,每次生产后就会消费。注意如果锁lock的值变了,调用notify 的时候会报异常!!!所以不能随意修改锁的值!
    例如
                String a = "1";
                synchronized (a){
                    System.out.println("1");
                    System.out.println("2");
                    a="12";
                    a.notify();
                    System.out.println("3");
                }
    运行就会报错:
    java.lang.IllegalMonitorStateException
    2
    	at java.lang.Object.notify(Native Method)
    	at thread.ThreadMain.main(ThreadMain.java:22)
    修改了String对象,原码中,String的修改是删除原对象,重新创建一个String对象,然后赋值。对应修改了String的存储地址,无法连接到原来的线程队列信息。如果使用数组或者其他类型就不会报错,因为我们只是修改对象里面的某个属性的值,但是对象自己本身的存储地址是不变的。
  • 如果有多个生产者多个消费者,容易出现假死状态,就是所有的线程全部进入wait状态,因为notify可能是消费者唤醒消费者,生产者唤醒生产者!
  • 为了解决多线程,消费者和生成者易出现死锁的状态,用操作栈来控制生成者和消费者的状态!使用list,里面只有有一个对象,判断他的长度为0或者1来控制等待和唤醒的对象.需要有一个线程栈来控制消费和生产!
  • 代码如下:
  • 线程栈类:
    public class ThreadStack {
        private List list = new ArrayList<>();
        synchronized  public void push(){
            try{
                while(list.size() == 1){
                    this.wait();
                }
                double value = Math.random();
                System.out.println("此时list的size:" + list.size()+"。生产的值为:" +value);
                list.add(value);
                this.notifyAll();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    
        synchronized  public String pop(){
            String returnValue = "";
            try{
                while(list.size() == 0){
                    this.wait();
                }
                returnValue = "" + list.get(0);
                System.out.println("此时list的size:" + list.size()+"。消费的值为:" + list.get(0));
                list.remove(0);
                this.notifyAll();
            }catch (Exception e){
                e.printStackTrace();
            }
            return returnValue;
        }
    }
    消费者:
    public class Consumer {
        ThreadStack threadStack;
    
        public Consumer(ThreadStack threadStack) {
            this.threadStack = threadStack;
        }
        public void popService(){
            threadStack.pop();
        }
    }
    生产者:
    public class Producer {
        private ThreadStack threadStack;
    
        public Producer(ThreadStack threadStack) {
            this.threadStack = threadStack;
        }
    
        public void pushService(){
            threadStack.push();
        }
    }
    生产者线程:
    public class ThreadProducer implements Runnable{
        private String threadName;
        private Producer producer;
    
        public ThreadProducer(String threadName, Producer producer) {
            this.threadName = threadName;
            this.producer = producer;
        }
    
        @Override
        public void run() {
            System.out.println("此时是线程:"+threadName);
            while (true){
                producer.pushService();
            }
        }
    }
    消费者线程:
    public class ThreadConsumer implements Runnable{
        private String threadName;
        private Consumer consumer;
    
        public ThreadConsumer(String threadName, Consumer consumer) {
            this.threadName = threadName;
            this.consumer = consumer;
        }
    
        @Override
        public void run() {
            System.out.println("此时是线程:"+threadName);
            while(true){
                consumer.popService();
            }
    
        }
    }
    主方法类:
    public class ThreadMain {
        public static void main(String[] args) {
            try {
    
                ThreadStack threadStack = new ThreadStack();
               Producer producer = new Producer(threadStack);
                Producer producer1 = new Producer(threadStack);
                Producer producer2 = new Producer(threadStack);
                Producer producer3 = new Producer(threadStack);
                Producer producer4 = new Producer(threadStack);
    
               Consumer consumer = new Consumer(threadStack);
               Consumer consumer1 = new Consumer(threadStack);
               Consumer consumer2 = new Consumer(threadStack);
               Consumer consumer3 = new Consumer(threadStack);
               Consumer consumer4 = new Consumer(threadStack);
    
    
                Thread threadProducer = new Thread(new ThreadProducer("生产者",producer));
                Thread threadProducer1 = new Thread(new ThreadProducer("生产者",producer1));
                Thread threadProducer2 = new Thread(new ThreadProducer("生产者",producer2));
                Thread threadProducer3 = new Thread(new ThreadProducer("生产者",producer3));
                Thread threadProducer4 = new Thread(new ThreadProducer("生产者",producer4));
                threadProducer.start();
                threadProducer1.start();
                threadProducer2.start();
                threadProducer3.start();
                threadProducer4.start();
    
                Thread threadConsumer = new Thread(new ThreadConsumer("消费者",consumer));
                Thread threadConsumer1 = new Thread(new ThreadConsumer("消费者1",consumer1));
                Thread threadConsumer2 = new Thread(new ThreadConsumer("消费者2",consumer2));
                Thread threadConsumer3 = new Thread(new ThreadConsumer("消费者3",consumer3));
                Thread threadConsumer4 = new Thread(new ThreadConsumer("消费者4",consumer4));
                threadConsumer.start();
                threadConsumer1.start();
                threadConsumer2.start();
                threadConsumer3.start();
                threadConsumer4.start();
    } catch (Exception e) { e.printStackTrace(); } }}在多线程判断中,都用while,而不用if!因为if只在进入循环体的时候判断一次条件,而while是每循环一次判断一次条件,对于多线程来说,每循环一次判断一次条件才是安全的!
    以上的消费者模式,是多生产多消费。总体思路是,只要list的长度为0时,允许你生产,长度为1时,允许你消费。

join
  • 在使用线程a的代码中使用了join,如果在join执行完前使用interrupt()会报异常!!InterruptedException()!!
  • join(long):a使用b.join(2000),就是b线程运行两秒后再执行接下来的代码。例如:
    public class JoinMain {
        public static void main(String[] args) {
            try {
                JoinThread joinThreadA = new JoinThread("A");
                JoinThread joinThreadB = new JoinThread("B");
                Thread threadA = new Thread(joinThreadA);
                Thread threadB = new Thread(joinThreadB);
                threadA.start();
                threadA.join(2000);
                threadB.start();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
  • threadB.start();会在threadA线程运行2秒后开始执行。
  • join(long)和sleep(long)的区别!
    • 1.join(long)内部是使用wait(long),所以会释放锁!和sleep是不释放锁的

ThreadLocal的使用

    为了让所有线程都使用同一个变量,使用public static修饰,如果想实现每一个线程都有自己的共享变量,就要使用ThreadLocal类。

    ThreadLocal主要解决的就是每个线程绑定自己的值。

方法:

    set(Object o)设置参数,

    get(Object o)获取参数

创建一个公共的静态的ThreadLocal对象

public class ThreadLocalTest {
    public static ThreadLocal t1 = new ThreadLocal();
}

线程类A

public class ThreadA  implements  Runnable{
    @Override
    public void run() {
        try{
            for (int i = 0; i < 100; i++) {
                ThreadLocalTest.t1.set("ThreadA"+i);
                System.out.println(ThreadLocalTest.t1.get());
                Thread.sleep(200);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
线程类B
public class ThreadB implements  Runnable{
    @Override
    public void run() {
        try{
            for (int i = 0; i < 100; i++) {
                ThreadLocalTest.t1.set("ThreadB"+i);
                System.out.println(ThreadLocalTest.t1.get());
                Thread.sleep(200);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
主方法类
public class ThreadMain {
    public static void main(String[] args) {
        ThreadA thread1 = new ThreadA();
        ThreadB thread2 = new ThreadB();
        Thread threadA = new Thread(thread1);
        Thread threadB = new Thread(thread2);
        threadA.start();
        threadB.start();
    }
}
运行结果:
ThreadA0
ThreadB0
ThreadA1
ThreadB1
ThreadA2
ThreadB2
ThreadA3
ThreadB3
ThreadA4
ThreadB4

看结果可知,线程相互之间完全不影响。

如果第一次没有设置值,你get到的是null。如果你想给初值可以重写initiaValue()方法!例如:

public class ThreadLocalExt extends  ThreadLocal{
    @Override
    protected Object initialValue() {
        return "我是初值";
    }
}
在创建ThreadLocal时,用ThreadLocalExt替代ThreadLocal就可以了。


InheritableThreadLocal的使用

    可以在字线程中取得父线程继承下来的值。

    Tools类

public class Tools {
    public  static InheritableThreadLocalExt t1 = new InheritableThreadLocalExt();
}
InheritableThreadLocalExt:
public class InheritableThreadLocalExt extends  InheritableThreadLocal {
    @Override
    protected Object initialValue() {
        return new Date().getTime();
    }
}
ThreadA
public class ThreadA  implements  Runnable{
    @Override
    public void run() {
        try{
            for (int i = 0; i < 100; i++) {
                System.out.println(Tools.t1.get());
                Thread.sleep(200);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
ThradB
public class ThreadB implements  Runnable{
    @Override
    public void run() {
        try{
            for (int i = 0; i < 100; i++) {
                System.out.println(Tools.t1.get());
                Thread.sleep(200);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
主方法:
public class ThreadMain {
    public static void main(String[] args) {
        System.out.println("主线程获得的值为:" + Tools.t1.get());
        ThreadA thread1 = new ThreadA();
        ThreadB thread2 = new ThreadB();
        Thread threadA = new Thread(thread1);
        Thread threadB = new Thread(thread2);
        threadA.start();
        threadB.start();
    }
}
运行结果:
主线程获得的值为:1526371963488
1526371963488
1526371963488
1526371963488
1526371963488
你会发现主线程和子线程的值的一样的!

同时也可以在子线程去修改这个值,通过重写InheritableThreadLocal的childValue()方法,其他地方不用变。

public class InheritableThreadLocalExt extends  InheritableThreadLocal {
    @Override
    protected Object initialValue() {
        return new Date().getTime();
    }

    @Override
    protected Object childValue(Object parentValue) {
        return parentValue + "我在子线程加的!!!";
    }
}

输出结果:

主线程获得的值为:1526372240305
1526372240305我在子线程加的!!!
1526372240305我在子线程加的!!!
1526372240305我在子线程加的!!!





    


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值