Why this isn't allowed
main is a method.  As with other programming languages, when a method returns, all of the variables declared in its body go out of scope, and accessing them has undefined behavior.  Under some circumstances, the memory location where they used to be will no longer be valid.
Obviously this is a problem.  If you try to change num after main has returned, you might overwrite a portion of the stack that doesn't belong to num anymore.  Java's response to this difficult situation is to introduce restrictions on how you can share variables: they must be final.  Java can then safely locate them in such a way that reading them will produce consistent results even after the function has returned.
The C equivalent to this problem is storing and using the address of a local variable outside of its scope, something that all C programmers are taught to never do.
To get around it, declare num as a member of test, create an instance, and pass that to it.  This removes the dependancy on a local variable, and thus removes the final restriction.
public class test
{
    int num = 111;
    public static void main(String[] args) throws Exception
    {
        test t = new test();
        (new Thread(t) {
            test mytest;
            Thread(test t)
            {
                mytest = t;
            }
            @Override
            public void run() {
                mytest.num = 222;
            }
        }).start();
    }
}