Core Java
- suppose every javaer need to know
Collection
I/O
File & Directory
- java.io.File
File file1 = new File("D:\\Document\\books\\java.pdf");
File file2 = new File("/opt/data/my.txt");
File file3 = new File("/opt/data", "my.txt");
- Paths of File
File file = new File("./pom.xml");
System.out.println("Absolute path = " + file.getAbsolutePath());
System.out.println("Canonical path = " + file.getCanonicalPath());
System.out.println("Name = " + file.getName());
System.out.println("Parent = " + file.getParent());
System.out.println("Path = " + file.getPath());
System.out.println("Is absolute = " + file.isAbsolute());
Output:
Absolute path = /Users/lenicliu/git/eg-java/eg-java-io/./pom.xml
Canonical path = /Users/lenicliu/git/eg-java/eg-java-io/pom.xml
Name = pom.xml
Parent = .
Path = ./pom.xml
Is absolute = false
- List Directories
File dir = new File(".");
File[] folders = dir.listFiles((f) -> f.isDirectory()); // java.io.FileFilter
for (File folder : folders) {
System.out.println(folder);
}
Output:
./.settings
./src
./target
RandomAccessFile
- Read & Write
RandomAccessFile file = new RandomAccessFile("user.dat", "rw");
file.writeUTF("username:lenicliu\n");
file.writeUTF("birthday:1987-09\n");
file.close();
// comment -> java.io.IOException: Stream Closed
file = new RandomAccessFile("user.dat", "r");
System.out.println(file.readLine());
System.out.println(file.readLine());
file.close();
InputStream & OutputStream
- InputStream & OutputStream is used to handle bytes
InputStream input = new ByteArrayInputStream(new byte[] { (byte) 4 });
int read = -1;
while ((read = input.read()) != -1) {
System.out.println(read); // output : 4
}
input.close();
OutputStream output = new ByteArrayOutputStream();
output.write(new byte[] { (byte) 2, (byte) 39 });
output.close();
- Read File & Write File
FileOutputStream output = new FileOutputStream("user.dat");
output.write("lenicliu, male".getBytes());
output.close();
FileInputStream input = new FileInputStream("user.dat");
byte[] buffer = new byte[input.available()];
input.read(buffer);
input.close();
System.out.println(new String(buffer));// Output : lenicliu, male
- IOException
- This is an example of OutputStream, InputStream is similar to it
FileOutputStream output = null;
try {
output = new FileOutputStream("user.dat");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
try {
output.write("lenicliu, male".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
// make sure stream closed
try {
if (output != null) {
output.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
- try-with-resources
try (FileInputStream input = new FileInputStream("user.dat")) {
byte[] buffer = new byte[input.available()];
input.read(buffer);
// input.close(); // it's unnecessary
System.out.println(new String(buffer));
} catch (IOException e) {
e.printStackTrace();
}
- Object Serialization and Deserialization
// 1) class User { // throw java.io.NotSerializableException
class User implements Serializable {
String name;
transient String sex; // 3) it will be lost
// 2) throw java.io.InvalidClassException if different value read
private static final long serialVersionUID = 2933636808737636316L;
public User(String name, String sex) {
super();
this.name = name;
this.sex = sex;
}
public String getName() {
return name;
}
public String getSex() {
return sex;
}
}
ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("user.dat"));
output.writeObject(new User("lenicliu", "male"));
output.close();
ObjectInputStream input = new ObjectInputStream(new FileInputStream("user.dat"));
User user = (User) input.readObject();
input.close();
System.out.println("name is " + user.getName());
System.out.println("sex is " + user.getSex());// 3) output : sex is null
Reader & Writer
- Reader & Writer are used to handle chars
- FileReader & FileWriter
- InputStreamReader & OutputStreamWriter
- BufferedReader & BufferedWriter
BufferedReader.readLine()
- FileReader & FileWriter
FileWriter writer = new FileWriter("user.dat");
writer.write("lenicliu, male");
writer.close();
FileReader reader = new FileReader("user.dat");
int read = -1;
while ((read = reader.read()) != -1) {
System.out.print((char) read);
}
System.out.println();
reader.close();
Note: Don't forget to handle IOException
Performance
- Create a big file by following command:
- mac :
mkfile -n 1g gigabyte.dat
- linux :
dd if=/dev/zero of=gigabyte.dat bs=1M count=1024
- win :
fsutil file createnew gigabyte.dat 1073741824
- mac :
- Let's do a simple experiment: Read a big file, 1G
private static void useFileReader(File dat, int size) throws FileNotFoundException, IOException {
long begin = System.currentTimeMillis();
FileReader reader = new FileReader(dat);
char[] buffer = new char[size];
while (reader.read(buffer) != -1) {
}
reader.close();
long end = System.currentTimeMillis();
System.out.println("FileReader : buffer size = " + size + "," + (end - begin) / 1000.0 + " seconds.");
}
private static void useFileInputStream(File dat, int size) throws FileNotFoundException, IOException {
long begin = System.currentTimeMillis();
FileInputStream input = new FileInputStream(dat);
byte[] buffer = new byte[size];
while (input.read(buffer) != -1) {
}
input.close();
long end = System.currentTimeMillis();
System.out.println("FileInputStream : buffer size = " + size + "," + (end - begin) / 1000.0 + " seconds.");
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
File dat = new File("gigabyte.dat");
useFileReader(dat, 512);
useFileReader(dat, 1024);
useFileReader(dat, 2048);
useFileReader(dat, 4096);
useFileReader(dat, 8192);
useFileInputStream(dat, 512);
useFileInputStream(dat, 1024);
useFileInputStream(dat, 2048);
useFileInputStream(dat, 4096);
useFileInputStream(dat, 8192);
}
Output:
FileReader : buffer size = 512, 1.08 seconds.
FileReader : buffer size = 1024, 0.937 seconds.
FileReader : buffer size = 2048, 0.895 seconds.
FileReader : buffer size = 4096, 0.893 seconds.
FileReader : buffer size = 8192, 0.884 seconds.
FileInputStream : buffer size = 512, 1.756 seconds.
FileInputStream : buffer size = 1024, 0.987 seconds.
FileInputStream : buffer size = 2048, 0.592 seconds.
FileInputStream : buffer size = 4096, 0.373 seconds.
FileInputStream : buffer size = 8192, 0.29 seconds.
Socket
final int port = 10000;
final String EOF = "\n";
// server thread
new Thread(() -> {
try {
ServerSocket server = new ServerSocket(port);
Socket socket = server.accept();
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String name = reader.readLine();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
writer.write("Hi, " + name + EOF);
writer.flush();
socket.close();
server.close();
} catch (Exception e) {
e.printStackTrace();
}
}).start();
// client thread
new Thread(() -> {
try {
Socket socket = new Socket("localhost", port);
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
writer.write("lenicliu" + EOF);
writer.flush();
System.out.println(reader.readLine()); // output : Hi, lenicliu
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}).start();
NIO
Buffers
Channels
- FileChannel
File src = new File("/path/of/srcFile");
File dest = new File("/path/of/destFile");
FileInputStream in = null;
FileOutputStream ot = null;
try {
in = new FileInputStream(src);
ot = new FileOutputStream(dest);
FileChannel ic = in.getChannel();
FileChannel oc = ot.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024 * 8);
buffer.clear();
while (ic.read(buffer) != -1) {
buffer.flip(); // flip before write
oc.write(buffer);
buffer.clear(); // make buffer empty for reading
}
} catch (IOException e) {
// handle exception
} finally {
if (in != null) {
try {
in.close();
} catch (IOException ignore) {
}
}
if (ot != null) {
try {
ot.close();
} catch (IOException ignore) {
}
}
}
- ServerSocketChannel & SocketChannel
final InetSocketAddress localhost = new InetSocketAddress("127.0.0.1", 10000);
new Thread(() -> {
try {
ServerSocketChannel server = ServerSocketChannel.open();
server.bind(localhost);
SocketChannel socket = server.accept();
ByteBuffer buffer = ByteBuffer.allocate(1024);
socket.read(buffer);
buffer.flip();
String name = new String(buffer.array());
System.out.println("server recieved: " + name);
socket.write(ByteBuffer.wrap(("Hi, " + name).getBytes()));
socket.close();
server.close();
} catch (Exception e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
SocketChannel socket = SocketChannel.open();
socket.connect(localhost);
socket.write(ByteBuffer.wrap("lenicliu".getBytes()));
ByteBuffer buffer = ByteBuffer.allocate(1024);
socket.read(buffer);
buffer.flip();
System.out.println("client recieved: " + new String(buffer.array()));
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}).start();
- DatagramChannel
final InetSocketAddress localhost = new InetSocketAddress("127.0.0.1", 10000);
new Thread(() -> {
try {
DatagramChannel server = DatagramChannel.open();
server.bind(localhost);
ByteBuffer buffer = ByteBuffer.allocate(1024);
SocketAddress address = server.receive(buffer);
buffer.flip();
String name = new String(buffer.array());
System.out.println("server recieved: " + name);
server.send(ByteBuffer.wrap(("Hi, " + name).getBytes()), address);
server.close();
} catch (Exception e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
DatagramChannel client = DatagramChannel.open();
client.send(ByteBuffer.wrap("lenicliu".getBytes()), localhost);
ByteBuffer buffer = ByteBuffer.allocate(1024);
client.receive(buffer);
buffer.flip();
System.out.println("client recieved: " + new String(buffer.array()));
client.close();
} catch (Exception e) {
e.printStackTrace();
}
}).start();
- Pipe
final Pipe pipe = Pipe.open();
new Thread(() -> {
try {
SinkChannel channel = pipe.sink();
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("Hi, lenicliu".getBytes());
buffer.flip();// flip before write
System.out.println("write: " + new String(buffer.array()));
channel.write(buffer);
channel.close();
} catch (Exception e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
SourceChannel channel = pipe.source();
ByteBuffer buffer = ByteBuffer.allocate(1024);
channel.read(buffer);
channel.close();
buffer.flip();
System.out.println("read: " + new String(buffer.array()));
} catch (Exception e) {
e.printStackTrace();
}
}).start();
Selectors
Thread & Concurrency
Thread & Runnable
- Thread is class, Runnable is interface
- Thread State
- NEW -> not started
- RUNNABLE -> running/executing
- BLOCKED -> blocked by synchronized, lock, etc...
- WAITING
- TIME_WAITING
- TERMINATED -> exited
- Daemon
- More
- Interrupting
new Thread().interrupt()
- Joining
new Thread().join()
- Sleeping
Thread.sleep(1000)
- Interrupting
Synchronization
- synchronized method
class Counter{
private int count = 0;
public synchronized int get(){
return count++;
}
}
- synchronized block
class Counter{
private int count = 0;
private Object lock = new Object();
public int get(){
synchronized(lock){
return count++;
}
}
}
- problem
- Deadlock : T1 & T2 waits for the resource that's held by each other
- Livelock : T1 keeps retrying operation that always fail, so can't make progress
- Starvation : T1 always denied to access the resource, so can't make progress
Waiting & Notification
- wait() cause the current thread to wait until another thread invokes the notify() or notifyAll()
- notify() wake up any one of threads that are waiting on this object's monitor
- both of wait() and notify() must be called from within a synchronized context
synchronized(obj){
while(){
obj.wait();
}
}
Producer & Consumer
class SharedInt {
int value = -1;
boolean available = false;
synchronized int get() {
while (!this.available) {
try {
this.wait();
} catch (InterruptedException e) {}
}
this.available = false;
this.notify();
return this.value;
}
synchronized void set(int value) {
while (this.available) {
try {
this.wait();
} catch (InterruptedException e) {}
}
this.value = value;
this.available = true;
this.notify();
}
}
SharedInt shared = new SharedInt();
Runnable producer = () -> {
int[] values = IntStream.range(0, 10).toArray();
for (int value : values) {
synchronized (shared) {
shared.set(value);
System.out.println("producer : " + value);
}
}
};
Runnable consumer = () -> {
while (true) {
synchronized (shared) {
int value = shared.get();
System.out.println("consumer : " + value);
if (value == 9) { break; }
}
}
};
new Thread(consumer).start();
new Thread(producer).start();
More
- Thread Groups
- Thread Local It's a separate storage slot to each thread
public class Main {
private static ThreadLocal messages = new ThreadLocal<>();
public static void main(String[] args) {
Runnable r = () -> {
// every thread could hold diff value here
messages.set(variable);
String value = messages.get();
};
// ...
}
}
- Timer
- Timer start a thread
- TaskQueue is a priority queue of TimerTasks ordered by
nextExecutiveTime
- TaskQueue is a priority queue of TimerTasks ordered by
- TimerTask implement Runnable
- Timer start a thread
Executor
- submit(Runnable) & submit(Callable)
- awaitTermination(long, TimeUnit)
- block until all tasks have finished after a shutdown() request.
- timeout & interrupt
Executor executor = (runnable) -> new Thread(runnable).start();
executor.execute(() -> System.out.println("Hello Executor"));
ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.execute(() -> System.out.println("Hello ExecutorService"));
executorService.shutdown();
Synchronizers
- Countdown Latches
CountDownLatch latch = new CountDownLatch(3);
Runnable r = () -> {
latch.countDown();
};
new Thread(r).start();
new Thread(r).start();
new Thread(r).start();
latch.await();
- Cyclic Barriers
Runnable finish = () -> {
System.out.println("S:" + System.currentTimeMillis());
};
// need 3 task reached the barrier, and then 'finish' will execute
CyclicBarrier barrier = new CyclicBarrier(3, finish);
Runnable worker = () -> {
try {
TimeUnit.SECONDS.sleep(2);// do sth
barrier.await();
} catch (Exception ignore) {
}
};
System.out.println("E:" + System.currentTimeMillis());
new Thread(worker).start();
new Thread(worker).start();
new Thread(worker).start();// commented -> endless
Output:
E:1461684706399
S:1461684708401
- Exchangers
- Semaphores
Semaphore semaphore = new Semaphore(3, true);
Runnable worker = () -> {
try {
semaphore.acquire();
// do sth.
} catch (InterruptedException ignore) {
} finally {
semaphore.release();
}
};
new Thread(worker).start();
new Thread(worker).start();
new Thread(worker).start();
// This one will wait for calling semaphore.release()
new Thread(worker).start();
- Phasers
Locking
- Lock core interface,
lock()
&unlock()
- ReentrantLock
ReentrantLock lock = new ReentrantLock();
Runnable worker = () -> {
lock.lock();
// lock.lock(); // uncomment -> endless
try {
TimeUnit.SECONDS.sleep(2);// do sth.
} catch (InterruptedException e) {
} finally {
lock.unlock();
}
};
- Condition
class SharedInt {
int value = -1;
boolean available = false;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
void lock() { lock.lock(); }
void unlock() { lock.unlock(); }
int get() {
lock.lock();
try {
while (!this.available) {
try { this.condition.await(); }
catch (InterruptedException e) {}
}
this.available = false;
this.condition.signal();
return this.value;
} finally {
lock.unlock();
}
}
void set(int value) {
lock.lock();
try {
while (this.available) {
try { this.condition.await(); }
catch (InterruptedException e) {}
}
this.value = value;
this.available = true;
this.condition.signal();
} finally {
lock.unlock();
}
}
}
SharedInt shared = new SharedInt();
Runnable producer = () -> {
int[] values = IntStream.range(0, 10).toArray();
for (int value : values) {
shared.lock();
shared.set(value);
System.out.println("producer : " + value);
shared.unlock();
}
};
Runnable consumer = () -> {
int value = -1;
while (value != 9) {
shared.lock();
value = shared.get();
System.out.println("consumer : " + value);
shared.unlock();
}
};
new Thread(consumer).start();
new Thread(producer).start();
- ReadWriteLock
- ReentrantReadWriteLock
class ReadWriteLockList {
List list = new ArrayList<>();
ReadWriteLock lock = new ReentrantReadWriteLock();
Lock rlock = lock.readLock();
Lock wlock = lock.writeLock();
public String get(int index) {
rlock.lock();
try {
return list.get(index);
} finally {
rlock.unlock();
}
}
public String remove(int index) {
wlock.lock();
try {
return list.remove(index);
} finally {
wlock.unlock();
}
}
// ...
}
More
- Concurrent Collections
- java.util.concurrent.BlockingQueue
- java.util.concurrent.ArrayBlockingQueue
- java.util.concurrent.LinkedBlockingQueue
- java.util.concurrent.PriorityBlockingQueue
- java.util.concurrent.SynchronousQueue
- java.util.concurrent.ArrayBlockingQueue
- java.util.concurrent.ConcurrentMap
- java.util.concurrent.ConcurrentHashMap
- java.util.concurrent.ConcurrentHashMap
- java.util.concurrent.BlockingQueue
- Atomic
class Counter{
private AtomicInteger count = new AtomicInteger(0);
public int get(){
return count.getAndIncrement();
}
}
- Completion Services
class CalculatePI implements Callable {
int n;
public CalculatePI(int n) {
this.n = n;
}
@Override
public BigDecimal call() throws Exception {
MathContext mc = new MathContext(100, RoundingMode.HALF_UP);
// pi = 1/1 - 1/3 + 1/7 - 1/9 + 1/11 - 1/13 .....
BigDecimal pi = BigDecimal.ZERO;
for (int i = 1; i <= n; i++) {
if (i % 2 == 1) {
pi = pi.add(BigDecimal.ONE.divide(new BigDecimal(2 * i - 1), mc));
} else {
pi = pi.subtract(BigDecimal.ONE.divide(new BigDecimal(2 * i - 1), mc));
}
}
return pi.multiply(new BigDecimal(4), mc);
}
}
ExecutorService es = Executors.newFixedThreadPool(8);
CompletionService cs = new ExecutorCompletionService(es);
long begin = System.currentTimeMillis();
cs.submit(new CalculatePI(10000000));
System.out.println(cs.take().get());
long end = System.currentTimeMillis();
System.out.println("Time:" + (end - begin));
es.shutdown();
Output:
3.141592553589793238462893383279502881072169399375201133474944586897660156286702367768659759356643435
Time:3896
- Fork & Join
class Factorial extends RecursiveTask {
final long start, end, threshold = 10;
public Factorial(long end) {
this(1, end);
}
private Factorial(long start, long end) {
this.start = start;
this.end = end;
}
@Override
protected BigDecimal compute() {
MathContext mc = new MathContext(100, RoundingMode.HALF_UP);
BigDecimal factorial = BigDecimal.ONE;
if (end - start < threshold) {
for (long i = start; i <= end; i++) {
factorial = factorial.multiply(new BigDecimal(i), mc);
}
} else {
long middle = (start + end) / 2;
Factorial left = new Factorial(start, middle);
Factorial right = new Factorial(middle + 1, end);
left.fork();
right.fork();
factorial = left.join().multiply(right.join());
}
return factorial;
}
}
ForkJoinPool forkJoinPool = new ForkJoinPool();
System.out.println(forkJoinPool.submit(new Factorial(100)).get());
- Use ForkJoinPool & RecursiveTask to enhance performance : PI Calculation
final MathContext mc = new MathContext(100, RoundingMode.HALF_UP);
class Pi extends RecursiveTask {
final long start, end, threshold = 10000;
public Pi(long end) {
this(1, end);
}
private Pi(long start, long end) {
this.start = start;
this.end = end;
}
@Override
protected BigDecimal compute() {
BigDecimal pi = BigDecimal.ZERO;
if (end - start < threshold) {
for (long i = start; i <= end; i++) {
if (i % 2 == 1) {
pi = pi.add(BigDecimal.ONE.divide(new BigDecimal(2 * i - 1), mc));
} else {
pi = pi.subtract(BigDecimal.ONE.divide(new BigDecimal(2 * i - 1), mc));
}
}
pi = pi.multiply(new BigDecimal(4), mc);
} else {
long middle = (start + end) / 2;
Pi left = new Pi(start, middle);
Pi right = new Pi(middle + 1, end);
left.fork();
right.fork();
pi = left.join().add(right.join());
}
return pi;
}
}
ForkJoinPool forkJoinPool = new ForkJoinPool();
long begin = System.currentTimeMillis();
System.out.println(forkJoinPool.submit(new Pi(10000000)).get());
long end = System.currentTimeMillis();
System.out.println("Time:" + (end - begin));
Output:
3.14159255358979323846289338327950288107216939937520113347494458689766015628670236776865975935664343500149358
Time:2053
Time reduced by 1 second