多线程学习

线程的创建

使用Thread创建线程

直接新建一个线程,并调用start()方法

    private void thread(){
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Thread with thread start!");
            }
        });
        thread.start();
    }
    // 或者使用给线程传入一个Runable对象
    private void runnable() {
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            System.out.println("Thread with runnable start!");
        }
    };
    Thread thread = new Thread(runnable);
    thread.start();
    }

使用ThreadFactory工厂类创建线程

使用AtomicInteger保证线程名称不重复,

    private void threadFactory() {
        AtomicInteger count = new AtomicInteger(0);
        ThreadFactory factory = new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r,"Thread-" + count.incrementAndGet());
            }
        };
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " start!");
            }
        };
        Thread thread = factory.newThread(runnable);
        thread.start();
    }

使用线程池创建或复用线程

当需要大量线程时,从性能内存的角度考虑应使用线程池,来避免大量临时对象的创建。

    private void execute() {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("Thread with runnable thread pool start!");
            }
        };
        // 创建一个有缓存的线程池
        Executor executor = Executors.newCachedThreadPool();
        // 创建只有一个线程的线程池
//        Executor executor = Executors.newSingleThreadExecutor();
        // 创建指定个数的线程池,适合短时间批量处理
//        Executor executor = Executors.newFixedThreadPool(5);
        // 创建一个可以延时处理或周期处理的线程池
//        ScheduledExecutorService executor = Executors.newScheduledThreadPool(5);
        executor.execute(runnable);
        executor.execute(runnable);
        executor.execute(runnable);
    }

线程同步和线程安全

synchronized

synchronized的本质:保证方法内部或代码块内部资源的互斥访问,即同一时间,最多只有一个线程在访问资源,保证线程之间对监视资源的数据同步,即,任何线程在获取到Monitor后第一时间将资源拷贝到自己的缓存中,在释放Monitor的第一时间将缓存中的资源数据拷贝到共享内存中,从而确保其他线程在获取到Monitor时,拷贝的都是最新的资源数据;

synchronized 实现死锁

死锁的四个必要条件:

  • 互斥条件:在一段时间内某资源只能由一个进程(线程)持有,此时若有其他进程(线程)请求该资源,则只能等待当前进程(线程)释放资源。
  • 不可剥夺条件:进程(线程)获得资源后,在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程(线程)自己释放;
  • 请求与保持条件:进程(线程)已经保持了一个资源,此时又请求其他资源,其他资源又被其他进程(线程)持有,导致进程(线程)被阻塞,并且对已保持资源不释放;
  • 循环等待条件:两个进程(线程)同时持有对方需要的资源,导致进程被阻塞,无法释放资源。
    private Object monitor1 = new Object();
    private Object monitor2 = new Object();

    private void creatDeadLock() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                deadLock(monitor1, monitor2);
            }
        }, "Thread 1").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                deadLock(monitor2, monitor1);
            }
        }, "Thread 2").start();
    }
    private void deadLock(Object object1, Object object2) {
        synchronized (object1) {
            try {
                Thread.sleep(500);
                System.out.println(Thread.currentThread().getName() + "持有:" + object1.hashCode() + ", 并请求: " + object2.hashCode());
                synchronized (object2) {
                    System.out.println("get object2 resource!");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    // 打印结果
    Thread 2持有:2089229631, 并请求: 2009125064
    Thread 1持有:2009125064, 并请求: 2089229631
    // Thread 1 和 Thread 2请求相互持有的资源,同时又保持着对方的资源不释放,导致程序死锁

volatile

保证加了volatile关键子的字段的操作具有原子性和同步性,原子性相当于实现了针对单一字段的线程间互斥访问。
volatile只对基本数据类型(byte short int long float boolean double char)的赋值操作和对象的引用赋值操作有效;

线程安全问题本质

在多个线程访问共同的资源时,在某个线程对资源进程写操作时(写操作一开始,还未结束),其他线程对这个写了一半的资源进行读取,或者对这个正在写的资源也进行了写操作,导致出现错误数据。

锁机制的本质

通过对共享资源进行访问限制,让同一时间只有一个线程可以访问资源,保证数据的准确性。

posted @ 2021-03-20 23:47  little_learner  阅读(41)  评论(0)    收藏  举报