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方法会使得已获取的对象锁的线程,放弃对象锁。

原文地址:https://www.cnblogs.com/hanfight/p/3951552.html