Jan 31, 2019 - Neat Java 8 features - Concurrency API

Comments

Neat Java 8 features - Concurrency API

This, the last of three posts is about the Java Concurrency API

Previous posts:

Concurrency API

The Concurrency API has been around for longer, but got some extra features with Java 8.

If you want to run tasks in parallel, wrap them either in a ‘Runnable’ (no return value) or a ‘Callable’ (has a return value). Submit these tasks to an ExecutorService and check the returned ‘Futures’ for completion, respectively get the results from them.

Here’s an example:

ExecutorService es = Executors.newWorkStealingPool();
List<Future<String>> futures = new ArrayList<Future<String>>();
for (int i=0; i<10; i++) {
    Callable<String> task = () -> {
    TimeUnit.SECONDS.sleep(1);
    return "This callable was executed by "
        + Thread.currentThread().getName();
    };
    futures.add(es.submit(task));
}
        
while(!futures.stream().allMatch(f -> f.isDone())) {
    TimeUnit.MILLISECONDS.sleep(100);
}
        
futures.stream().forEach(f ->  { 
    try {
        System.out.println(f.get());
     } catch (Exception e) {
         e.printStackTrace();
     }
});

Here’s another example using a CompletionService. With the CompletionService you don’t have to loop over the Futures and check if they are ready, you can simple take the next one which is ready from the service.

Random random = new Random();
        
CompletionService<String> cs = new ExecutorCompletionService<String>(Executors.newWorkStealingPool());
        
for (int i=0; i<10; i++) {
    Callable<String> task = () -> {
    // wait for 1 to 10 sec
    TimeUnit.SECONDS.sleep(random.nextInt(10)+1);
    return "This callable was executed by "
        + Thread.currentThread().getName();
    };
    cs.submit(task);
}

int completed = 0;
while (completed < 10) {
    // take the next one which is ready
    Future<String> fut = cs.take();
    System.out.println("Another one completed: "+fut.get());
    completed++;
}

Jan 22, 2019 - Virtualbox tips and tricks

Comments

First things to do on a fresh virtualbox guest

This is for a Debian 9 system; might also work on newer versions and Ubuntu.

Installing ‘guest additions’

You need to install the ‘build essentials’ first:

apt-get -y install build-essential module-assistant
m-a prepare

‘Insert’ the guest addtions CD. The I usually copy the contents over into the root directory and run it from there.

mount /media/cdrom0
cp -r /media/cdrom0 /root/
cd /root/cdrom0
./VBoxLinuxAdditions.run

Setup access to the ‘shared’ directory

The user needs to be in the ‘vboxsf’ group:

usermod -a -G vboxsf [USERNAME]

I also usually create a symlink in the user’s home directory for simplicity:

ln -s /media/sf_[SHARE NAME] /home/[USERNAME]/[SHARE NAME]

Here’s everything wrapped up in a script:

#!/bin/bash
if [[ $EUID -ne 0 ]]
then
	echo "This script must run as root user"
	exit 1
fi

echo "Installing stuff needed for virtualbox guest additions installation"

apt-get -y install build-essential module-assistant
m-a prepare

echo "Setting up permissions for shared directory"
echo "Shared directory name: (just hit return to skip this step if you don't have one)"
read dirname

if [[ ! -z "$dirname" ]]
then
	echo "Your username:"
	read username
	
	echo "Adding user to vboxsf group"
	usermod -a -G vboxsf $username
	
	link=/home/$username/$dirname
	if [[ ! -L ${link} ]]
	then
		echo "Creating symlink $link to shared directory"
		ln -s /media/sf_$dirname $link
		chown $username:$username $link
	else
		echo "$link already exists."
fi

echo "Installing vbox guest additions (if CD is inserted)"
if mount /media/cdrom0
then
	cp -r /media/cdrom0 /root/
	cd /root/cdrom0
	./VBoxLinuxAdditions.run
	cd ..
	rm -rf /root/cdrom0
	echo "Finished. Hit return to reboot."
	read xxx
	shutdown -r now
else
	echo "vbox guest additions CD not found."
	echo "Finished."

Oct 3, 2018 - Neat Java 8 features - Stream API

Comments

Neat Java 8 features - Stream API

This is the second of three posts and is about the Stream API.

Previous post: Neat Java 8 features - Lambda expressions Next post: Neat Java 8 features - Concurrency API

Stream API

These methods make it easier to work with Collections. What are you typically doing with a collection? You’re iterating over it, in order modify certain/all objects, find specific objects, filter the collection, sort the collection, again and again… And you always start with for(...) {...} or while(...) {...}.

For example if you have a Collection of Items

    List<Item> collection = new ArrayList<Item>();
    for (int i=0; i < 1000; i++)
        collection.add(Item.randomInstance());

    static class Item {
        private static Random rand = new Random();
        
        String name;
        double price;

        Item(String name, double price) {
            this.name = name;
            this.price = price;
        }
        
        static Item randomInstance() {
            String name = UUID.randomUUID().toString().substring(0, 5);
            double amount = rand.nextDouble()*10;
            return new Item(name, amount);
         }
    }

A common task is to filter this collection:

    List<Item> filtered = new ArrayList<Item>();
    for(Item i : collection) {
        if (i.price > 5)
            filtered.add(i);
    }
    System.out.println(filtered.size()+" items are more expensive than 5.00 whatever." );

With the stream methods you can do this as a one liner:

    filtered = collection.stream().filter(i -> i.price > 5).collect(Collectors.toList());
    System.out.println(filtered.size()+" items are more expensive than 5.00 whatever." );

You can also do some more fancy things without all the usual for/while loop bloat:

    // Get the names of all items with a price > 9.5
    // as alphabetically sorted list
    List<String> names = collection.stream()
                         .filter(i -> i.price > 9.5)
                         .map(i -> i.name)
                         .sorted(String::compareTo)
                         .collect(Collectors.toList());
    // print the list
    names.stream().forEach(System.out::println);

Apart from the simplification of the code a big advantage of the stream API is, that it is very easy to parallelize tasks. If you have for example a fairly expensive operation which does something with your Item:

    Function<Item, Item> expensiveOperation = i -> {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
        }
        i.name = i.name.toUpperCase();
        return i;
    };

…and you want to run that over the whole collection, you could use the for loop:

    long t = System.currentTimeMillis();
    for(Item i : collection) {
        expensiveOperation.apply(i);
    }
    t = System.currentTimeMillis() - t;
    System.out.println("for loop took "+t+" ms.");
    
    // output: for loop took 11793 ms.

Or you could very easily parallelize it and make use of all your CPU cores:

    long t = System.currentTimeMillis();
    collection.parallelStream().map(expensiveOperation).collect(Collectors.toList());
    t = System.currentTimeMillis() - t;
    System.out.println("parallelStream() took "+t+" ms.");
    
    // output: parallelStream took 1498 ms.

But there is a caveat, that’s why I said use parallelStream() only for ‘fairly expensive’ operations:

  • Parallelization comes with an overhead. If the operation is very simple and quick, the sequential for loop is probably faster than Java having to deal with the parallelization.
  • The stream API uses a common thread pool. If it’s possible that the operation could take a long time to finish, you easily could use up all the available threads of the pool and block other faster / more important parallel tasks.