JVM

JAVA多线程:状态转换以及基本操作

2018-09-10  本文已影响225人  barry_di

一、CPU、进程、线程

二、线程调度

2.1什么是线程调度

线程调度就是系统为线程分配执行时间的过程。

2.2 线程调度的方式

根据线程调度的控制权是由系统控制或者线程本身来控制划分为:协同式的线程调度和抢占式的线程调度。
1、协同式线程调度:线程之间的系统执行时间,由线程本身进行进行控制。这种线程调度方式就像接力赛,一个执行完毕后交由下一个接力。如当前线程执行完毕后,通知系统调度到其他线程执行。
(1)协同的好处:线程的切换是可预知的。线程之间不存在同步的问题。
(2)协同的坏处:协同调度的致命缺点是当某个线程执行有问题的时候,会导致整个运行阻塞和系统崩溃。

2、抢占式线程调度:线程之间的系统执行时间,是由系统进行控制。而抢占式的线程调度对线程的不可预知,系统定期的中断当前正在执行的线程,将CPU执行权切换到下一个等待的线程。所以任何一个线程都不能独占CPU。正因为这种定期的线程切换导致线程之间存在不同的问题。当线程执行过程中,某个线程出现问题的时候,由线程对CPU不具有独占性。因此不会造成阻塞。

我们所使用的操作系统都是是用抢占性的线程调度。如果使用协同式的线程调度情况下,如果我们再使用某个软件出现问题时候,操作系统处于阻塞状态,导致整个操作系统崩溃,我们肯定会抓狂。

3、JAVA线程调度
Java线程调度就是抢占式调度。

三、Java线程的实现方式

JAVA提供了3中创建线程的方式:

   private static class TheadExtends extends Thread{
       @Override
       public void run() {
           System.out.println("TheadExtends");
       }
   }
    private static class RunnableImpl implements Runnable{
        @Override
        public void run() {
            System.out.println("RunnableImpl");
        }
    }
public class ThreadImplement {

    private static class CallableImpl implements Callable<String>{

        @Override
        public String call() throws Exception {
            Thread.sleep(2000);
            return "Callable";
        }
    }

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        
        CallableImpl callableImpl=new CallableImpl();
        FutureTask<String> futureTask = new FutureTask<String>(callableImpl);
        Thread  CallableThread= new Thread(futureTask);
        CallableThread.start();
        System.out.println(futureTask.get());
    }
}

四、JAVA线程状态转换

4.1Java线程状态转换图
image.png
4.2Java线程状态

JAVA线程状态包括:

五、多线程编程

5.1 多线程编程的好处
5.2 多线程带来的问题

七、线程基本操作

interrupt
    public static class SafeEndRunnable implements Runnable{

        @Override
        public void run() {
            System.out.println("flag = "+Thread.currentThread().isInterrupted());
            while(!Thread.currentThread().isInterrupted()) {
                System.out.println(Thread.currentThread().getName()+"running");
            }
            System.out.println(Thread.currentThread().getName()+"is end ,flag = "+Thread.currentThread().isInterrupted());
        }
    }

    
    public static void main(String[] args) throws InterruptedException {
        SafeEndRunnable safeEndRunnable = new SafeEndRunnable();
        Thread t1 = new Thread(safeEndRunnable);
        t1.start();
        Thread.sleep(1);
        t1.interrupt();
}
输出:
flag = false
Thread-0running
Thread-0running
Thread-0running
Thread-0running
Thread-0is end ,flag = true
方法名 方法类型 Demo 描述
isInterrupted 对象方法 Thread.currentThread().isInterrupted() 判断当前线程是否处于中断状态
interrupt 对象方法 Thread.currentThread().interrupt() 设置标志位为true
interrupted 静态方法 Thread.interrupted() 判断当前线程是否处于中断状态并且设置中断状态为false
public static class SafeEndThread implements Runnable{
        @Override
        public void run() {
            while(!Thread.currentThread().isInterrupted()) {
                System.out.println(Thread.currentThread().getName()+"running");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e){
                    e.printStackTrace();
                    System.out.println("flag = "+Thread.currentThread().isInterrupted());
                    Thread.currentThread().interrupt();
                }
            }
            System.out.println(Thread.currentThread().getName()+"is end ,flag = "+Thread.currentThread().isInterrupted());          
        }
            
    public static void main(String[] args) throws InterruptedException {
        SafeEndThread safeEndThread = new SafeEndThread();
        Thread t2 = new Thread(safeEndThread);
        t2.start(); 
        Thread.sleep(1);
        t2.interrupt();
    }
输出:
Thread-0running
java.lang.InterruptedException: sleep interrupted
flag = false
Thread-0is end ,flag = true
yield

yield的主要作用的是让出CPU的执行时间,需要注意的时候,调用yield虽然让出了CPU的执行时间,但是会参与下一次的CPU执行时间的竞争中,如果当前线程重新获得CPU执行时间,那么当前的线程再次执行。如下:

public static class ThreadYieldRunnable implements Runnable{

        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                System.out.println(Thread.currentThread().getName()+"running"+i);
                Thread.yield();
            }
        }
    }
    
    public static void main(String[] args) {
        ThreadYieldRunnable threadYieldRunnable= new ThreadYieldRunnable();
        Thread t1 = new Thread(threadYieldRunnable);
        t1.start();
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t1.yield");
    }

输出:
Thread-0running0
Thread-0running1
t1.yield
Thread-0running2
Thread-0running3
Thread-0running4
Thread-0running5
Thread-0running6
Thread-0running7
Thread-0running8
Thread-0running9
Thread-0running10
Thread-0running11
Thread-0running12
Thread-0running13
Thread-0running14
Thread-0running15
Thread-0running16
Thread-0running17
Thread-0running18
Thread-0running19

六、线程共享

锁的主要作用是保护临界区资源,在多线程访问临界区时互斥。那么在线程访问共享的资源时,JAVA提供了以下保存线程之间的线程共享资源。

Synchronized
方式 锁对象 Demo
对象同步 当前对象 synchronized void demo()
静态同步 当前类 static synchronized void demo()
代码块 当前对象、其他对象、类 Demo demo = new Demo();
synchronized (demo) {}
synchronized (this){}
synchronized(Demo.class) {}
    public static void main(String[] args) {
        Object lock1 = new Object();
        Object lock2 = new Object();
        Thread t1 = new Thread(() -> {
            synchronized (lock1) {
                System.out.println("Thread1 get locke1");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock2) {
                    System.out.println("Thread1 get locke2");
                }
            }
        });

        Thread t2 = new Thread(() -> {
            synchronized (lock2) {
                System.out.println("Thread2 get locke2");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock1) {
                    System.out.println("Thread2 get locke1");
                }
            }

        });
        t1.start();
        t2.start();
    }
public class ConstLock implements Runnable {
    private Object  lock ;
    
    public ConstLock(Object lock) {
        super();
        this.lock = lock;
    }

    public void run() {
        synchronized (lock) {
            while(true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"sayHello");
            }
        }
    }
    
    public static void main(String[] args) {
        ConstLock constLock1 = new ConstLock("lock");
        ConstLock constLock2 = new ConstLock("lock");
        Thread t1 = new Thread(constLock1,"Thread1");
        Thread t2 = new Thread(constLock2,"Thread2");
        t1.start();
        t2.start();
    }
    public static void main(String[] args) {
        ConstLock constLock1 = new ConstLock(21);
        ConstLock constLock2 = new ConstLock(21);
        Thread t1 = new Thread(constLock1,"Thread1");
        Thread t2 = new Thread(constLock2,"Thread2");
        t1.start();
        t2.start();
    }
    
    public static void main(String[] args) {
        ConstLock constLock1 = new ConstLock(21l);
        ConstLock constLock2 = new ConstLock(21l);
        Thread t1 = new Thread(constLock1,"Thread1");
        Thread t2 = new Thread(constLock2,"Thread2");
        t1.start();
        t2.start();
    }

}

打印:
Thread1sayHello
Thread1sayHello
Thread1sayHello

volatile关键字

volatile是JAVA中提供的一种轻量级的同步机制。而这种轻量级的同步机制是通过线程之间的通讯来保证。而不是通过锁的机制进行处理。因此不会对执行的线程造成阻塞。

public class Reorder {

    public static int x = 0;
    public static int y = 0;
    public static int a = 0;
    public static int b = 0;

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(()->{
            a = 1;
            x = b;
        }) ;
        Thread thread2 = new Thread(()->{
            b = 1;
            y = a;
        });
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println("x=" + x + ";y=" + y);
    }
}

如没有禁止指令重排序就会出现:x=1;y=0、x=0;y=1、x=1;y=1、x=0;y=0四种结果。


image.png

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 10000; i++)
                        counter++;
                }
            });
            thread.start();
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(counter);
    }

(2)确保只有一个线程修改变量的值的情况。

ThreadLocal

ThreadLocal是一个线程本地存储,而每个线程有自己独立的副本。也就是说每个线程可以存放各自变量到线程本地存储中,并且线程之间各自访问各自的线程本地存储。当线程结束后,线程的本地存储会被垃圾回收。如果线程本地存储中的变量被其他引用到的情况下,是不会被回收。我们可以把ThreadLocal看作一个Map<Thread,Object>。

static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
        protected Integer initialValue() {
            return 1;
        };
    };

    public void startThread() {
        for (int i = 0; i < 2; i++) {
            Thread t1 = new Thread(new ThreadLocalRunnable(i));
            t1.start();
        }

    }

    @Data
    @AllArgsConstructor
    public static class ThreadLocalRunnable implements Runnable {

        private int id;

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + "  id=" + id);
            int beforeId = threadLocal.get();
            int afterId = beforeId + id;
            threadLocal.set(afterId);
            System.out.println(Thread.currentThread().getName() + "  after id =" + threadLocal.get());
        }

    }

    public static void main(String[] args) {
        new UseThreadLocal().startThread();
    }
输出:
Thread-0  id=0
Thread-0  after id =1
Thread-1  id=1
Thread-1  after id =2

参考资料:

《深入理解Java虚拟机》

《Java特种兵》5.1 基础介绍

上一篇 下一篇

猜你喜欢

热点阅读