zoukankan      html  css  js  c++  java
  • Java线程

    Java线程启动方式

    在Java中有两种方式可以启动线程,一种方式是通过继承Thread类,另一种方式通过继承Runnable接口。

    public class MyThread extends Thread {
    
        @Override
        public void run() {
            // TODO Auto-generated method stub
            for(int i = 0; i < 10; ++i) {
                System.out.println(i);
            }
        }
        
        public static void main(String []args) {
            MyThread thread = new MyThread();
            /**线程的启动的通过调用start方法,直接调用run方法无异于调用类中的方法*/
            thread.start();
        }
        
    }
    public class MyRunnable implements Runnable {
    
        @Override
        public void run() {
            // TODO Auto-generated method stub
            for(int i = 0 ; i < 10; ++i) {
                System.out.println(i);
            }
        }
    
        public static void main(String []args) {
            MyRunnable task = new MyRunnable();
            Thread thread = new Thread(task);
            thread.start();
        }
    }

     

    创建出Thread类的对象之后通过调用start方法启动线程的运行,而不是run方法。当线程的代码逻辑执行完毕之后,线程会自动结束(就是说当程序执行完run方法体后,线程结束)。值得注意的是,对Java来说,run方法没有任何特别之处,像main方法一样,它只是新线程知道调用的方法名称。因此,在Runnable上或者Thread上调用run方法是合法的,但这并不启动新的线程。

    Java线程的同步机制

    在Java虚拟机中,每个类和对象在逻辑上都有一个与之关联的监视器,该监视器便是获取类和对象的锁,在任何时候只允许一个线程拥有类和对象的锁。

    类锁实际上也是通过对象锁(Class对象)来实现的,当虚拟机加载一个class文件时,会创建一个java.lang.Class类的实例,当锁住类时,实质上是锁住的该类对应的Class对象。

    线程可以对同一个对象上锁,对于每一个对象,Java虚拟机维护一个加锁计数器,线程每次获得该对象锁时,计数器就加一,释放锁时,计数器就减一,只有当计数器为零时,锁就被完全释放。

    public class NumberTask implements Runnable {
    
        public String num;
        
        public Demo main;
        
        public NumberTask(String num, Demo main) {
            this.num = num;
            this.main = main;
        }
        
        @Override
        public void run() {
            try {
                main.run(num);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
    }
    
    
    public class Demo {
        
        public void run(String threadNum) throws InterruptedException {
            for (int i = 0; i < 5; i++) {
                System.out.println("[" + threadNum + "] " + i);
            }
        }
        
        
        public static void main(String []args) {
            Demo ma = new Demo();
            ExecutorService es = Executors.newFixedThreadPool(2);
            for (int i = 0; i < 2; i++) {
                String num = "THREAD_" + String.format("%02d", i+1);
                es.submit(new NumberTask(num, ma));
            }
        }
        
    }

    在上述代码中,NumberTask中run方法调用Demo类中run,run方法没有添加synchronized关键字,也就是允许多个线程进入该方法,因此输出结果如下:

    [THREAD_01] 0
    [THREAD_01] 1
    [THREAD_01] 2
    [THREAD_02] 0
    [THREAD_02] 1
    [THREAD_02] 2
    [THREAD_02] 3
    [THREAD_01] 3
    [THREAD_02] 4
    [THREAD_01] 4

    Java中synchronized关键字,用来控制线程之间同步的,如果在Demo类中的run方法添加synchronized关键字,则只是允许一个线程访问该方法,因此结果输出如下:

    [THREAD_01] 0
    [THREAD_01] 1
    [THREAD_01] 2
    [THREAD_01] 3
    [THREAD_01] 4
    [THREAD_02] 0
    [THREAD_02] 1
    [THREAD_02] 2
    [THREAD_02] 3
    [THREAD_02] 4

    使用关键词synchronized主要用来实现线程之间的互斥,即同一时刻只有一个线程允许执行特定的代码。通过互斥的方法来保证多个线程访问共享变量时的正确性。除了互斥访问之外,线程之间也需要通过协作的方式来完成某些任务。此时可以使用Object类提供的wait、notify和notifyAll方法。

    public class Cache {
    
        private static final Integer SIZE = 10;
        
        private static List<Integer> caches = new ArrayList<Integer>();
        
        private static int index = 0;
        
        public static void putDate(Integer integer) throws InterruptedException {
            synchronized (caches) {
                if (caches.size() > SIZE) {  // 缓冲区尺寸大于10,线程等待
                    caches.wait();           // 释放caches对象锁
                } 
                caches.add(index++,integer);
                System.out.println("[" + Thread.currentThread().getName() + "]生成数据" 
                        + integer);
                caches.notify();             // 唤醒等待线程
            }
        }
        
        public static void getDate() throws InterruptedException {
            synchronized (caches) {
                if (caches.size() <= 0) {
                    caches.wait();          // 释放caches对象锁
                }
                System.out.println("[" + Thread.currentThread().getName() + 
                        "]获取数据" + caches.remove(--index));
                caches.notify();
            }
        }
    }
    
    
    public class DateMakeThread extends Thread {
    
        public DateMakeThread(String name) {
            super(name);
        }
        
        @Override
        public void run() {
            while(true) {
                try {
                    Cache.getDate();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        
    }
    
    
    public class DateTakerThread extends Thread {
        
        public DateTakerThread(String name) {
            super(name);
        }
    
        @Override
        public void run() {
            Random random = new Random();
            for (int i = 0; i < 10; i++) {
                try {
                    Cache.putDate(random.nextInt(100));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
    }
    
    
    public class Main {
    
        public static void main(String []args) {
            DateMakeThread dmt = new DateMakeThread("PRODUCER");
            dmt.start();
            DateTakerThread dtt = new DateTakerThread("CONSUMER");
            dtt.start();
        }
        
    }

    在上述代码,Cache中"生产者-消费者"的缓冲区,DateMakeThread生成数据存入Cache中,Cache类中caches对象是互斥访问的,DateTakerThread用来在Cache中获取数据,值得注意的是:wait,notify,notifyAll方法的使用的前提是获得对象锁,wait方法会使得已获取的对象锁的线程,放弃对象锁。

  • 相关阅读:
    karto 资料
    底盘运动学
    QT中搜索文件列表
    Qt中引入boost库
    AGV调度理论链接
    qt开发杂记
    C++代码读取ping的网络延时
    结构体解析数据
    QJson 的简单使用
    Vue中问题总结 与未解决问题总结
  • 原文地址:https://www.cnblogs.com/hanfight/p/3951552.html
Copyright © 2011-2022 走看看