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方法会使得已获取的对象锁的线程,放弃对象锁。
以上是 Java线程 的全部内容, 来源链接: utcz.com/z/393487.html