Core Java

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
  • 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)

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
    • TimerTask implement Runnable

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.ConcurrentMap
      • java.util.concurrent.ConcurrentHashMap
  • 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

你可能感兴趣的:(Core Java)