`
vitoer
  • 浏览: 6304 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Java进阶(三)多线程开发关键技术

 
阅读更多
本文转发自Jason’s Blog,原文链接 http://www.jasongj.com/java/multi_thread/

sleep和wait到底什么区别
其实这个问题应该这么问——sleep和wait有什么相同点。因为这两个方法除了都能让当前线程暂停执行完,几乎没有其它相同点。

wait方法是Object类的方法,这意味着所有的Java类都可以调用该方法。sleep方法是Thread类的静态方法。

wait是在当前线程持有wait对象锁的情况下,暂时放弃锁,并让出CPU资源,并积极等待其它线程调用同一对象的notify或者notifyAll方法。注意,即使只有一个线程在等待,并且有其它线程调用了notify或者notifyAll方法,等待的线程只是被激活,但是它必须得再次获得锁才能继续往下执行。换言之,即使notify被调用,但只要锁没有被释放,原等待线程因为未获得锁仍然无法继续执行。测试代码如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import java.util.Date;
public class Wait {
  public static void main(String[] args) {
    Thread thread1 = new Thread(() -> {
      synchronized (Wait.class) {
        try {
          System.out.println(new Date() + " Thread1 is running");
          Wait.class.wait();
          System.out.println(new Date() + " Thread1 ended");
        } catch (Exception ex) {
          ex.printStackTrace();
        }
      }
    });
    thread1.start();
   
    Thread thread2 = new Thread(() -> {
      synchronized (Wait.class) {
        try {
          System.out.println(new Date() + " Thread2 is running");
          Wait.class.notify();
          // Don't use sleep method to avoid confusing
          for(long i = 0; i < 200000; i++) {
            for(long j = 0; j < 100000; j++) {}
          }
          System.out.println(new Date() + " Thread2 release lock");
        } catch (Exception ex) {
          ex.printStackTrace();
        }
      }
     
      for(long i = 0; i < 200000; i++) {
        for(long j = 0; j < 100000; j++) {}
      }
      System.out.println(new Date() + " Thread2 ended");
    });
   
    // Don't use sleep method to avoid confusing
    for(long i = 0; i < 200000; i++) {
      for(long j = 0; j < 100000; j++) {}
    }
    thread2.start();
  }
}
执行结果如下

1
2
3
4
5
Tue Jun 14 22:51:11 CST 2016 Thread1 is running
Tue Jun 14 22:51:23 CST 2016 Thread2 is running
Tue Jun 14 22:51:36 CST 2016 Thread2 release lock
Tue Jun 14 22:51:36 CST 2016 Thread1 ended
Tue Jun 14 22:51:49 CST 2016 Thread2 ended
从运行结果可以看出

thread1执行wait后,暂停执行
thread2执行notify后,thread1并没有继续执行,因为此时thread2尚未释放锁,thread1因为得不到锁而不能继续执行
thread2执行完synchronized语句块后释放锁,thread1得到通知并获得锁,进而继续执行
注意:wait方法需要释放锁,前提条件是它已经持有锁。所以wait和notify(或者notifyAll)方法都必须被包裹在synchronized语句块中,并且synchronized后锁的对象应该与调用wait方法的对象一样。否则抛出IllegalMonitorStateException

sleep方法告诉操作系统至少指定时间内不需为线程调度器为该线程分配执行时间片,并不释放锁(如果当前已经持有锁)。实际上,调用sleep方法时并不要求持有任何锁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.test.thread;
import java.util.Date;
public class Sleep {
  public static void main(String[] args) {
    Thread thread1 = new Thread(() -> {
      synchronized (Sleep.class) {
        try {
          System.out.println(new Date() + " Thread1 is running");
          Thread.sleep(2000);
          System.out.println(new Date() + " Thread1 ended");
        } catch (Exception ex) {
          ex.printStackTrace();
        }
      }
    });
    thread1.start();
   
    Thread thread2 = new Thread(() -> {
      synchronized (Sleep.class) {
        try {
          System.out.println(new Date() + " Thread2 is running");
          Thread.sleep(2000);
          System.out.println(new Date() + " Thread2 ended");
        } catch (Exception ex) {
          ex.printStackTrace();
        }
      }
     
      for(long i = 0; i < 200000; i++) {
        for(long j = 0; j < 100000; j++) {}
      }
    });
   
    // Don't use sleep method to avoid confusing
    for(long i = 0; i < 200000; i++) {
      for(long j = 0; j < 100000; j++) {}
    }
    thread2.start();
  }
}
执行结果如下

1
2
3
4
Thu Jun 16 19:46:06 CST 2016 Thread1 is running
Thu Jun 16 19:46:08 CST 2016 Thread1 ended
Thu Jun 16 19:46:13 CST 2016 Thread2 is running
Thu Jun 16 19:46:15 CST 2016 Thread2 ended
由于thread 1和thread 2的run方法实现都在同步块中,无论哪个线程先拿到锁,执行sleep时并不释放锁,因此其它线程无法执行。直到前面的线程sleep结束并退出同步块(释放锁),另一个线程才得到锁并执行。

注意:sleep方法并不需要持有任何形式的锁,也就不需要包裹在synchronized中。

synchronized几种用法
每个Java对象都可以用做一个实现同步的互斥锁,这些锁被称为内置锁。线程进入同步代码块或方法时自动获得内置锁,退出同步代码块或方法时自动释放该内置锁。进入同步代码块或者同步方法是获得内置锁的唯一途径。

实例同步方法
synchronized用于修饰实例方法(非静态方法)时,执行该方法需要获得的是该类实例对象的内置锁(同一个类的不同实例拥有不同的内置锁)。如果多个实例方法都被synchronized修饰,则当多个线程调用同一实例的不同同步方法(或者同一方法)时,需要竞争锁。但当调用的是不同实例的方法时,并不需要竞争锁。

静态同步方法
synchronized用于修饰静态方法时,执行该方法需要获得的是该类的class对象的内置锁(一个类只有唯一一个class对象)。调用同一个类的不同静态同步方法时会产生锁竞争。

同步代码块
synchronized用于修饰代码块时,进入同步代码块需要获得synchronized关键字后面括号内的对象(可以是实例对象也可以是class对象)的内置锁。

synchronized使用总结
锁的使用是为了操作临界资源的正确性,而往往一个方法中并非所有的代码都操作临界资源。换句话说,方法中的代码往往并不都需要同步。此时建议不使用同步方法,而使用同步代码块,只对操作临界资源的代码,也即需要同步的代码加锁。这样做的好处是,当一个线程在执行同步代码块时,其它线程仍然可以执行该方法内同步代码块以外的部分,充分发挥多线程并发的优势,从而相较于同步整个方法而言提升性能。

释放Java内置锁的唯一方式是synchronized方法或者代码块执行结束。若某一线程在synchronized方法或代码块内发生死锁,则对应的内置锁无法释放,其它线程也无法获取该内置锁(即进入跟该内置锁相关的synchronized方法或者代码块)。

Java中的锁
重入锁
Java中的重入锁(即ReentrantLock)与Java内置锁一样,是一种排它锁。使用synchronized的地方一定可以用ReentrantLock代替。

重入锁需要显示请求获取锁,并显示释放锁。为了避免获得锁后,没有释放锁,而造成其它线程无法获得锁而造成死锁,一般建议将释放锁操作放在finally块里,如下所示。

1
2
3
4
5
6
try{
  renentrantLock.lock();
  // 用户操作
} finally {
  renentrantLock.unlock();
}
如果重入锁已经被其它线程持有,则当前线程的lock操作会被阻塞。除了lock()方法之外,重入锁(或者说锁接口)还提供了其它获取锁的方法以实现不同的效果。

lockInterruptibly() 该方法尝试获取锁,若获取成功立即返回;若获取不成功则阻塞等待。与lock方法不同的是,在阻塞期间,如果当前线程被打断(interrupt)则该方法抛出InterruptedException。该方法提供了一种解除死锁的途径。
tryLock() 该方法试图获取锁,若该锁当前可用,则该方法立即获得锁并立即返回true;若锁当前不可用,则立即返回false。该方法不会阻塞,并提供给用户对于成功获利锁与获取锁失败进行不同操作的可能性。
tryLock(long time, TimeUnit unit) 该方法试图获得锁,若该锁当前可用,则立即获得锁并立即返回true。若锁当前不可用,则等待相应的时间(由该方法的两个参数决定):1)若该时间内锁可用,则获得锁,并返回true;2)若等待期间当前线程被打断,则抛出InterruptedException;3)若等待时间结束仍未获得锁,则返回false。
重入锁可定义为公平锁或非公平锁,默认实现为非公平锁。

公平锁是指多个线程获取锁被阻塞的情况下,锁变为可用时,最新申请锁的线程获得锁。可通过在重入锁(RenentrantLock)的构造方法中传入true构建公平锁,如Lock lock = new RenentrantLock(true)
非公平锁是指多个线程等待锁的情况下,锁变为可用状态时,哪个线程获得锁是随机的。synchonized相当于非公平锁。可通过在重入锁的构造方法中传入false或者使用无参构造方法构建非公平锁。
读写锁
如上文《Java进阶(二)当我们说线程安全时,到底在说什么》所述,锁可以保证原子性和可见性。而原子性更多是针对写操作而言。对于读多写少的场景,一个读操作无须阻塞其它读操作,只需要保证读和写或者写与写不同时发生即可。此时,如果使用重入锁(即排它锁),对性能影响较大。Java中的读写锁(ReadWriteLock)就是为这种读多写少的场景而创造的。

实际上,ReadWriteLock接口并非继承自Lock接口,ReentrantReadWriteLock也只实现了ReadWriteLock接口而未实现Lock接口。ReadLock和WriteLock,是ReentrantReadWriteLock类的静态内部类,它们实现了Lock接口。

一个ReentrantReadWriteLock实例包含一个ReentrantReadWriteLock.ReadLock实例和一个ReentrantReadWriteLock.WriteLock实例。通过readLock()和writeLock()方法可分别获得读锁实例和写锁实例,并通过Lock接口提供的获取锁方法获得对应的锁。

读写锁的锁定规则如下:

获得读锁后,其它线程可获得读锁而不能获取写锁
获得写锁后,其它线程既不能获得读锁也不能获得写锁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package com.test.thread;
import java.util.Date;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockDemo {
  public static void main(String[] args) {
    ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    new Thread(() -> {
      readWriteLock.readLock().lock();
      try {
        System.out.println(new Date() + "\tThread 1 started with read lock");
        try {
          Thread.sleep(2000);
        } catch (Exception ex) {
        }
        System.out.println(new Date() + "\tThread 1 ended");
      } finally {
        readWriteLock.readLock().unlock();
      }
    }).start();
    new Thread(() -> {
      readWriteLock.readLock().lock();
      try {
        System.out.println(new Date() + "\tThread 2 started with read lock");
        try {
          Thread.sleep(2000);
        } catch (Exception ex) {
        }
        System.out.println(new Date() + "\tThread 2 ended");
      } finally {
        readWriteLock.readLock().unlock();
      }
    }).start();
    new Thread(() -> {
      Lock lock = readWriteLock.writeLock();
      lock.lock();
      try {
        System.out.println(new Date() + "\tThread 3 started with write lock");
        try {
          Thread.sleep(2000);
        } catch (Exception ex) {
          ex.printStackTrace();
        }
        System.out.println(new Date() + "\tThread 3 ended");
      } finally {
        lock.unlock();
      }
    }).start();
  }
}
执行结果如下

1
2
3
4
5
6
Sat Jun 18 21:33:46 CST 2016  Thread 1 started with read lock
Sat Jun 18 21:33:46 CST 2016  Thread 2 started with read lock
Sat Jun 18 21:33:48 CST 2016  Thread 2 ended
Sat Jun 18 21:33:48 CST 2016  Thread 1 ended
Sat Jun 18 21:33:48 CST 2016  Thread 3 started with write lock
Sat Jun 18 21:33:50 CST 2016  Thread 3 ended
从上面的执行结果可见,thread 1和thread 2都只需获得读锁,因此它们可以并行执行。而thread 3因为需要获取写锁,必须等到thread 1和thread 2释放锁后才能获得锁。

条件锁
条件锁只是一个帮助用户理解的概念,实际上并没有条件锁这种锁。对于每个重入锁,都可以通过newCondition()方法绑定若干个条件对象。

条件对象提供以下方法以实现不同的等待语义

await() 调用该方法的前提是,当前线程已经成功获得与该条件对象绑定的重入锁,否则调用该方法时会抛出IllegalMonitorStateException。调用该方法外,当前线程会释放当前已经获得的锁(这一点与上文讲述的Java内置锁的wait方法一致),并且等待其它线程调用该条件对象的signal()或者signalAll()方法(这一点与Java内置锁wait后等待notify()或notifyAll()很像)。或者在等待期间,当前线程被打断,则wait()方法会抛出InterruptedException并清除当前线程的打断状态。
await(long time, TimeUnit unit) 适用条件和行为与await()基本一致,唯一不同点在于,指定时间之内没有收到signal()或signalALL()信号或者线程中断时该方法会返回false;其它情况返回true。
awaitNanos(long nanosTimeout) 调用该方法的前提是,当前线程已经成功获得与该条件对象绑定的重入锁,否则调用该方法时会抛出IllegalMonitorStateException。nanosTimeout指定该方法等待信号的的最大时间(单位为纳秒)。若指定时间内收到signal()或signalALL()则返回nanosTimeout减去已经等待的时间;若指定时间内有其它线程中断该线程,则抛出InterruptedException并清除当前线程的打断状态;若指定时间内未收到通知,则返回0或负数。
awaitUninterruptibly() 调用该方法的前提是,当前线程已经成功获得与该条件对象绑定的重入锁,否则调用该方法时会抛出IllegalMonitorStateException。调用该方法后,结束等待的唯一方法是其它线程调用该条件对象的signal()或signalALL()方法。等待过程中如果当前线程被中断,该方法仍然会继续等待,同时保留该线程的中断状态。
awaitUntil(Date deadline) 适用条件与行为与awaitNanos(long nanosTimeout)完全一样,唯一不同点在于它不是等待指定时间,而是等待由参数指定的某一时刻。
调用条件等待的注意事项

调用上述任意条件等待方法的前提都是当前线程已经获得与该条件对象对应的重入锁。
调用条件等待后,当前线程让出CPU资源。
上述等待方法结束后,方法返回的前提是它能重新获得与该条件对象对应的重入锁。如果无法获得锁,仍然会继续等待。这也是awaitNanos(long nanosTimeout)可能会返回负值的原因。
一旦条件等待方法返回,则当前线程肯定已经获得了对应的重入锁。
重入锁可以创建若干个条件对象,signal()和signalAll()方法只能唤醒相同条件对象的等待。
一个重入锁上可以生成多个条件变量,不同线程可以等待不同的条件,从而实现更加细粒度的的线程间通信。
signal()与signalAll()

signal() 若有一个或若干个线程在等待该条件变量,则该方法会唤醒其中的一个(具体哪一个,无法预测)。调用该方法的前提是当前线程持有该条件变量对应的锁,否则抛出IllegalMonitorStateException。
signalALL() 若有一个或若干个线程在等待该条件变量,则该方法会唤醒所有等待。调用该方法的前提是当前线程持有该条件变量对应的锁,否则抛出IllegalMonitorStateException。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.test.thread;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionTest {
  public static void main(String[] args) throws InterruptedException {
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    new Thread(() -> {
      lock.lock();
      try {
        System.out.println(new Date() + "\tThread 1 is waiting");
        try {
          long waitTime = condition.awaitNanos(TimeUnit.SECONDS.toNanos(2));
          System.out.println(new Date() + "\tThread 1 remaining time " + waitTime);
        } catch (Exception ex) {
          ex.printStackTrace();
        }
        System.out.println(new Date() + "\tThread 1 is waken up");
      } finally {
        lock.unlock();
      }
    }).start();
   
    new Thread(() -> {
      lock.lock();
      try{
        System.out.println(new Date() + "\tThread 2 is running");
        try {
          Thread.sleep(4000);
        } catch (Exception ex) {
          ex.printStackTrace();
        }
        condition.signal();
        System.out.println(new Date() + "\tThread 2 ended");
      } finally {
        lock.unlock();
      }
    }).start();
  }
}
执行结果如下

1
2
3
4
5
Sun Jun 19 15:59:09 CST 2016  Thread 1 is waiting
Sun Jun 19 15:59:09 CST 2016  Thread 2 is running
Sun Jun 19 15:59:13 CST 2016  Thread 2 ended
Sun Jun 19 15:59:13 CST 2016  Thread 1 remaining time -2003467560
Sun Jun 19 15:59:13 CST 2016  Thread 1 is waken up
从执行结果可以看出,虽然thread 2一开始就调用了signal()方法去唤醒thread 1,但是因为thread 2在4秒钟后才释放锁,也即thread 1在4秒后才获得锁,所以thread 1的await方法在4秒钟后才返回,并且返回负值。

信号量Semaphore
信号量维护一个许可集,可通过acquire()获取许可(若无可用许可则阻塞),通过release()释放许可,从而可能唤醒一个阻塞等待许可的线程。

与互斥锁类似,信号量限制了同一时间访问临界资源的线程的个数,并且信号量也分公平信号量与非公平信号量。而不同的是,互斥锁保证同一时间只会有一个线程访问临界资源,而信号量可以允许同一时间多个线程访问特定资源。所以信号量并不能保证原子性。

信号量的一个典型使用场景是限制系统访问量。每个请求进来后,处理之前都通过acquire获取许可,若获取许可成功则处理该请求,若获取失败则等待处理或者直接不处理该请求。

信号量的使用方法

acquire(int permits) 申请permits(必须为非负数)个许可,若获取成功,则该方法返回并且当前可用许可数减permits;若当前可用许可数少于permits指定的个数,则继续等待可用许可数大于等于permits;若等待过程中当前线程被中断,则抛出InterruptedException。
acquire() 等价于acquire(1)。
acquireUninterruptibly(int permits) 申请permits(必须为非负数)个许可,若获取成功,则该方法返回并且当前可用许可数减permits;若当前许可数少于permits,则继续等待可用许可数大于等于permits;若等待过程中当前线程被中断,继续等待可用许可数大于等于permits,并且获取成功后设置线程中断状态。
acquireUninterruptibly() 等价于acquireUninterruptibly(1)。
drainPermits() 获取所有可用许可,并返回获取到的许可个数,该方法不阻塞。
tryAcquire(int permits) 尝试获取permits个可用许可,如果当前许可个数大于等于permits,则返回true并且可用许可数减permits;否则返回false并且可用许可数不变。
tryAcquire() 等价于tryAcquire(1)。
tryAcquire(int permits, long timeout, TimeUnit unit) 尝试获取permits(必须为非负数)个许可,若在指定时间内获取成功则返回true并且可用许可数减permits;若指定时间内当前线程被中断,则抛出InterruptedException;若指定时间内可用许可数均小于permits,则返回false。
tryAcquire(long timeout, TimeUnit unit) 等价于tryAcquire(1, long timeout, TimeUnit unit)*
release(int permits) 释放permits个许可,该方法不阻塞并且某线程调用release方法前并不需要先调用acquire方法。
release() 等价于release(1)。
注意:与wait/notify和await/signal不同,acquire/release完全与锁无关,因此acquire等待过程中,可用许可满足要求时acquire可立即返回,而不用像锁的wait和条件变量的await那样重新获取锁才能返回。或者可以理解成,只要可用许可满足需求,就已经获得了锁。
分享到:
评论

相关推荐

    java多线程进阶

    这本书“java多线程进阶”显然旨在帮助读者深化这方面的理解,打通编程中的“任督二脉”,使开发者能够更加熟练地在并发环境中编写高效且稳定的代码。 1. **线程基础**:书中首先会介绍Java多线程的基础知识,包括...

    java进阶篇主要内容的PPT

    本PPT详细阐述了Java进阶学习的关键概念和技术,旨在帮助读者深化对Java编程的理解,提升开发技能。这个资料涵盖了不仅限于教科书的知识,确保你能够掌握到实际工作中可能遇到的实用技巧和高级特性。 **1. 高级数据...

    Java进阶学习资料.zip

    在Java进阶学习中,你需要掌握以下几个关键的知识点: 1. 高级数据结构与算法:理解并能熟练运用ArrayList、LinkedList、HashMap等集合框架,深入研究Set、Queue、Stack等接口及其实现类。同时,学习并应用高级排序...

    Java进阶.zip

    "Java进阶"这个主题涵盖了Java开发中的高级概念和技术,旨在帮助程序员深入理解Java的精髓,提升开发技能。SunJava.pdf可能包含的是由Sun Microsystems(Java的原始开发者)发布的官方文档或教程。 1. **内存管理与...

    Java进阶100例

    《Java进阶100例》是一本专为Java初学者设计的学习教程,旨在帮助从零开始的人系统地掌握Java编程技术。这个压缩包包含了"编程实践:Java进阶100例.pdf"这一核心资料,它将通过一系列具体实例,深入浅出地讲解Java...

    java进阶篇课后编程习题答案

    1. **多线程**:Java提供了丰富的多线程支持,包括Thread类和Runnable接口。理解线程同步机制如`synchronized`关键字、锁(ReentrantLock等)、信号量(Semaphore)和条件变量(Condition)是进阶学习的重要部分。...

    java进阶之路 。。

    ### Java进阶之路:程序员成长的关键阶段与学习策略 在IT行业中,Java作为一种广泛使用的编程语言,不仅是初学者的首选,也是资深开发者的长期伙伴。本文将深入探讨Java程序员成长的三个关键阶段——初级阶段、中级...

    JAVA进阶架构思维导图.rar

    4. **多线程编程**: 学习并发编程的基础,如线程同步(锁、信号量、条件变量)、线程池、并发容器(ConcurrentHashMap、CopyOnWriteArrayList)。 5. **网络编程**: 理解TCP/IP协议,掌握Socket编程,了解HTTP协议...

    Java进阶资料

    在Java进阶学习中,我们会深入探讨以下几个关键领域: 1. 高级数据结构与算法:理解并掌握Java中的ArrayList、LinkedList、HashMap等集合框架的实现原理和性能特性,以及如何有效地运用它们来解决复杂问题。同时,...

    Java进阶诀窍

    以下是一些关键的Java进阶知识点,旨在帮助你提升技能水平,成为一名更优秀的Java开发者。 1. **多线程**:Java是多线程编程的首选语言,理解和熟练使用`Thread`类、`Runnable`接口、`ExecutorService`和`Future`是...

    java进阶提高学习教程-13JVM与多线程.pptx

    Java进阶学习教程中的第13章主要涵盖了JVM(Java虚拟机)和多线程两个核心概念。JVM是Java程序运行的基础,它负责解释执行字节码文件,并提供了Java语言的跨平台特性。Java程序通过JVM与操作系统交互,使得程序可以...

    Java进阶路线

    ### Java进阶路线详解 #### 一、Java基础 **1. 传值与传引用** 在Java中,基本类型(如int、char等)的传递是按值传递的,而对象类型的传递则是按引用传递的。理解这一点对于正确处理变量和对象之间的交互至关...

    java进阶(华师大)

    总之,Java进阶课程全面覆盖了构建复杂系统所需的关键技术,这些技术对于提升开发者技能、理解和解决问题的能力至关重要。通过学习和掌握这些概念,开发者可以更好地适应不断变化的软件开发环境,创造出更高质量的...

    推荐优质Java课程 疯狂Java语言编程 Java入门到进阶教程 16.多线程(共44页).ppt

    多线程”这一部分,主要探讨了Java中的并发编程,这是多核处理器环境下提高程序性能的关键技术。首先,讲解了线程的基本概念,线程是进程内的执行单元,是操作系统分配CPU时间片的基本单位,每个线程有自己的程序...

    Java 7编程高级进阶

    《Java 7编程高级进阶》是一本专为有经验的Java开发者设计的教程,旨在深化对Java 7特性和高级编程技术的理解。通过深入学习这本书,你可以掌握Java 7中的新特性,优化你的代码,并提升开发效率。下面将详细探讨Java...

    深入学习:Java多线程编程

    《深入学习:Java多线程编程》是一本专注于Java并发技术的专业书籍,旨在帮助开发者深入理解和熟练运用Java中的多线程编程。Java多线程是Java编程中的核心部分,尤其在现代高性能应用和分布式系统中不可或缺。理解并...

    JAVA进阶资料 进阶资料

    【Java进阶资料详解】 Java作为一款广泛应用的编程语言,其进阶学习涵盖了多个关键领域。这份资料集合了关于数据库管理、内存管理、服务器配置、性能优化以及消息中间件等多个主题,旨在帮助开发者深入理解Java技术...

    Java进阶学习网络服务器编程.rar

    总之,Java进阶学习网络服务器编程涵盖了广泛的知识点,包括基础的网络原理、Java的网络API、多线程、NIO、Web服务、RESTful设计、安全性和测试方法。通过深入学习和实践,你可以掌握构建高性能、高可用性的网络...

    Java 进阶之路(黑夜版)

    《Java进阶之路(黑夜版)》不仅是一本学习指南,更是每一位Java学习者的良师益友。它通过深入浅出的方式讲解了Java的核心知识,并结合作者丰富的实战经验和幽默风趣的语言风格,使得学习过程既轻松又高效。对于希望...

Global site tag (gtag.js) - Google Analytics