Java 多线程

目录

Java创建线程的3种方式

使用方式

线程中断

 使用方式

 常用方法和状态

线程优先级

守护线程

使用方式

synchronized

volatile

ThreadLocal<>

使用方式

wait & notify & notifyAll

wait/notifyAll 简单实现连接池

Join

调用yield() 、sleep()、wait()、notify()等方法对锁有何影响?




Java创建线程的3种方式

  1. extends Thread
  2. implements Runnable
  3. implements Callable<>
    private static class UserThread extends Thread{
        @Override
        public void run() {
            System.out.println("this is UserThread");
        }
    }

    private static class UserRunnable implements Runnable{

        @Override
        public void run() {
            System.out.println("this is UserRunnable");

        }
    }

    private static class UserCallable implements Callable<String>{
        @Override
        public String call() throws Exception {
            System.out.println("this is UserRunnable");
            return "callable result";
        }
    }

 

使用方式

   public static void main(String[] args) throws ExecutionException, InterruptedException {
        UserThread thread = new UserThread();
        thread.start();

        Thread MyRunnable = new Thread(new UserRunnable());
        MyRunnable.start();

        //FutureTask implements Runnable
        FutureTask<String> futureTask = new FutureTask<>(new UserCallable());
        Thread MyCallable = new Thread(futureTask);
        MyCallable.start();
        //get方法为阻塞方法,等待结果返回
        System.out.println(futureTask.get());
    }

线程中断

中断不是停止线程,中断的作用是将线程从sleep阻塞状态 转换成 就绪状态.

  • interrupt() 中断该线程
  • isInterrupted() 判断该线程是否处于中断状态
  • static 方法 interrupted() 将中断状态修改为false

 使用方式

    private static class MyThread extends Thread {
        @Override
        public void run() {
            //获取当前线程名称
            String threadName = Thread.currentThread().getName();
            while (!isInterrupted()) {//isInterrupted()判断该线程是否处于中断状态
                System.out.println(threadName + "is running");
            }
            System.out.println(threadName + " Interrupt flag is " + isInterrupted());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyThread thread = new MyThread();
        thread.start();
        Thread.sleep(2000);
        //中断该线程
        thread.interrupt();
    }

运行结果:

Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 Interrupt flag is true

 常用方法和状态

线程优先级

范围:1~10,默认为5,10为最高值(不可靠,跟操作系统有关,有的设置则不会起作用)

thread.setPriority(5);

守护线程

与主线程共死,finally不能保证一定执行。

使用方式

public class DaemonThread {
    private  static class MyThread extends Thread{
        @Override
        public void run() {
            try {
                //获取当前线程名称
                String threadName = Thread.currentThread().getName();
                while (!isInterrupted()) {//isInterrupted()判断该线程是否处于中断状态
                    System.out.println(threadName + " is running");
                }
                System.out.println(threadName + " Interrupt flag is " + isInterrupted());
            }finally {
                System.out.println("...........finally");
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        MyThread thread = new MyThread();
        //设置thread为main线程的守护线程,当main线程结束时thread也会随即结束
        thread.setDaemon(true);
        thread.start();
        Thread.sleep(5);
    }
}

运行结果:

Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running

注意:finally代码块没有执行。

synchronized

  • 对象锁
  • 类锁
    //对象锁
    public synchronized String sync1(){
        return "";
    }

    //类锁
    public static synchronized String sync2(){
        return "";
    }

 对象锁: 同一个对象的所有 synchronized方法(非static synchronized方法)公用一把锁(互斥)

类锁:类class的锁

注意:synchronized方法 和 static synchronized方法同时被调用时,不会出现互斥现象

volatile

只能保证可见性,不能保证原子性.

适用于:只有一个线程写,多个线程读的场景

不能保证原子性 --- 例子:volatile的变量,被多个线程同时引用该值,并且分部改变其值

public class VolatileDemo {
    private volatile int count = 10;

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    private static class MyThread extends Thread {
        private VolatileDemo volatileDemo;

        public MyThread(VolatileDemo volatileDemo) {
            this.volatileDemo = volatileDemo;
        }

        @Override
        public void run() {
            try {
                String threadName = Thread.currentThread().getName();
                int count = volatileDemo.getCount();
                System.out.println(threadName + "-count:" + count);
                //休眠2秒后在更新count值
                Thread.sleep(2000);
                volatileDemo.setCount(count + 10);
                System.out.println(threadName + "-count:" + volatileDemo.getCount());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private static class MyThread2 extends Thread {
        private VolatileDemo volatileDemo;

        public MyThread2(VolatileDemo volatileDemo) {
            this.volatileDemo = volatileDemo;
        }

        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            int count = volatileDemo.getCount();
            System.out.println(threadName + "-count:" + count);
            volatileDemo.setCount(count + 30);
            System.out.println(threadName + "-count:" + volatileDemo.getCount());
        }
    }

    public static void main(String[] args) {
        VolatileDemo demo = new VolatileDemo();
        MyThread thread = new MyThread(demo);
        MyThread2 thread2 = new MyThread2(demo);
        thread.start();
        thread2.start();
    }
}

运行结果:

Thread-0-count:10
Thread-1-count:10
Thread-1-count:40
Thread-0-count:20

根据结果可以发现,在Thread1修改count值后,count值为40(此时count = 40已经处于可见状态),但是Thread在Thread1改变之前已经获取到count = 10,经过修改变成count = 20.根据此示例可以看出volatile不满足原子性。

ThreadLocal<>

ThreadLocal是用来提供线程内部的共享变量,在多线程环境下,可以保证各个线程之间的变量互相隔离、相互独立.

使用方式

public class ThreadLocalDemo {
    static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 1;
        }
    };

    static class MyThread extends Thread{
        private Integer id;

        public MyThread(Integer id){
            this.id = id;
        }

        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            Integer result = threadLocal.get() + id;
            threadLocal.set(result);
            System.out.println(threadName + ": threalocal value is "+ threadLocal.get());
            threadLocal.remove();
        }
    }

    public static void main(String[] args) {
        MyThread thread1 = new MyThread(1);
        MyThread thread2 = new MyThread(2);
        MyThread thread3 = new MyThread(3);
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

运行结果:

Thread-0: threalocal value is 2
Thread-2: threalocal value is 4
Thread-1: threalocal value is 3

wait & notify & notifyAll

Object的wait方法可以将其置成阻塞状态,需要调用notify 或者 notifyAll将其唤醒

建议:唤醒建议使用notifyAll,如果使用notify,可能会导致唤醒型号丢失。

例如:

1.使用notify

同一个对象有多个线程的处理逻辑处于wait状态,当使用该对象的notify时,只会唤醒其中一个wait线程,可能唤醒的线程不是我们需要的wait线程。

public class WaitNotifyAllDemo {
    private final static String CITY = "XianYang";
    private String site;
    private Integer km;

    public WaitNotifyAllDemo() {
    }

    public WaitNotifyAllDemo(String site, Integer km) {
        this.site = site;
        this.km = km;
    }

    public synchronized void waitKm() {
        while (this.km <= 100) {
            try {
                this.wait();
                System.out.println("check km Thread[" + Thread.currentThread().getId() + "] is notified");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("the km is" + this.km + ",I will change db.");
    }

    public synchronized void waitSite() {
        while (CITY.equals(this.site)) {
            try {
                this.wait();
                System.out.println("check site Thread[" + Thread.currentThread().getId() + "] is notified");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("the km is" + this.site + ",I will change db.");
    }

    public synchronized void changeSite() {
        this.site = "Beijing";
        this.notify();
    }

    public synchronized void changeKm() {
        this.km = 101;
        this.notifyAll();
    }
}

public class TestWaitNotifyDemo {
    private static WaitNotifyAllDemo demo = new WaitNotifyAllDemo("XianYang", 100);

    private static class CheckKM extends Thread {
        @Override
        public void run() {
            demo.waitKm();
        }
    }

    private static class CheckSite extends Thread {
        @Override
        public void run() {
            demo.waitSite();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 3; i++) {
            new CheckKM().start();
        }

        for (int i = 0; i < 3; i++) {
            new CheckSite().start();
        }
        Thread.sleep(1000);
        demo.changeSite();
    }
}

运行结果:我们改变的site,却唤醒了km的等待线程,导致唤醒型号丢失

check km Thread[11] is notified

2.notifyAll

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 3; i++) {
            new CheckKM().start();
        }

        for (int i = 0; i < 3; i++) {
            new CheckSite().start();
        }
        Thread.sleep(1000);
        demo.changeKm();
    }

运行结果:由运行结果可以发现唤醒了该对象所有处于wait线程

check site Thread[16] is notified
check km Thread[12] is notified
the km is101,I will change db.
check site Thread[14] is notified
check site Thread[15] is notified
check km Thread[13] is notified
the km is101,I will change db.
check km Thread[11] is notified
the km is101,I will change db.

wait/notifyAll 简单实现连接池

public class MyConnection implements Connection {
    public static MyConnection getInstance(){
        return new MyConnection();
    }
    //省略需要重写Connection方法
}
public class MyPool {
    //自定义容器
    private static LinkedList<Connection> pool = new LinkedList<>();
    //初始化
    public MyPool(int initialSize) {
        if (initialSize > 0) {
            for (int i = 0; i < initialSize; i++) {
                pool.addLast(new MyConnection());
            }
        }
    }
    
    //获取连接池
    public Connection getConnection(long mills) throws InterruptedException {
        Connection connection = null;
        synchronized (pool) {
            if (mills < 0) {
                while (pool.isEmpty()) {
                    pool.wait();
                }
                connection = pool.removeFirst();
            } else {
                long overTime = System.currentTimeMillis() + mills;
                long remainTime = mills;
                while (pool.isEmpty() && remainTime > 0) {
                    pool.wait(remainTime);
                    remainTime = overTime - System.currentTimeMillis();
                }
                if (!pool.isEmpty()) {
                    connection = pool.removeFirst();
                }
            }
        }
        return connection;
    }
    
    //释放资源到连接池
    public void releaseConnection(Connection connection) {
        if (connection != null){
            synchronized (pool) {
                pool.addLast(connection);
                pool.notifyAll();
            }
        }
    }
}

public class PoolTest {
    static MyPool pool = new MyPool(10);
    static CountDownLatch countDownLatch = null;

    public static void main(String[] args) throws InterruptedException {
        int threadSize = 50;
        countDownLatch = new CountDownLatch(threadSize);
        int count = 20;//每个线程的操作次数
        AtomicInteger got = new AtomicInteger();//计数器:统计可以拿到连接的线程
        AtomicInteger notGot = new AtomicInteger();//计数器:统计没有拿到连接的线程
        for (int i = 0; i < threadSize; i++) {
            Thread thread = new Thread(new Work(count, got, notGot), "worker_" + i);
            thread.start();
        }
        countDownLatch.await();
        System.out.println("总共尝试了: " + (threadSize * count));
        System.out.println("拿到连接的次数:  " + got);
        System.out.println("没能连接的次数: " + notGot);
    }

    static class Work implements Runnable {
        int count;
        AtomicInteger got;
        AtomicInteger notGot;

        public Work(int count, AtomicInteger got, AtomicInteger notGot) {
            this.count = count;
            this.got = got;
            this.notGot = notGot;
        }

        @Override
        public void run() {
            while (count > 0) {
                try {
                    Connection connection = pool.getConnection(1000);
                    if (connection != null) {
                        try {
                            connection.createStatement();
                            connection.commit();
                        } finally {
                            pool.releaseConnection(connection);
                            got.incrementAndGet();
                        }
                    } else {
                        notGot.incrementAndGet();
                        System.out.println(Thread.currentThread().getName()
                                + "等待超时!");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    count--;
                }
            }
            countDownLatch.countDown();
        }
    }
}

Join

面试点:

线程A如何在线程B之后执行?

线程A执行了线程B的join方法,线程A必须要等待B执行完成之后,线程A才能继续自己的工作。

public class JoinThreadDemo {

    static class JoinThread implements Runnable {
        Thread previous;

        public JoinThread(Thread previous) {
            this.previous = previous;
        }

        @Override
        public void run() {
            try {
                previous.join();
                System.out.println(Thread.currentThread().getName() + " is end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread current = Thread.currentThread();
        for (int i = 0; i < 10; i++) {
            //每一个线程调用前一个线程的join方法
            Thread thread = new Thread(new JoinThread(current), i + "");
            thread.start();
            current = thread;
        }
        Thread.sleep(1000);
        System.out.println(Thread.currentThread().getName() + " is end");
    }
}

运行结果:

main is end
0 is end
1 is end
2 is end
3 is end
4 is end
5 is end
6 is end
7 is end
8 is end
9 is end

调用yield() 、sleep()、wait()、notify()等方法对锁有何影响?

面试点

线程在执行yield()以后,持有的锁是不释放的

sleep()方法被调用以后,持有的锁是不释放的

调动方法之前,必须要持有锁。调用了wait()方法以后,锁就会被释放,当wait方法返回的时候,线程会重新持有锁

调动方法之前,必须要持有锁,调用notify()/notifyAll()方法本身不会释放锁的(所以一般在方法末尾在调用)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值