We were just down for 56 hours due to our 3rd party platform vendor applying that type of update.
AFAICT the only workaround is to do it in steps: set the DEFAULT, fill existing rows with the default, then apply the NOT NULL constraint (which will still lock it for a full table scan to check the validity of the constraint).
If you add a 'not null default something' to the column, it'll rewrite the whole table, which can take some time. And since the table has the AccessExclusive lock, this is what will block reads/writes.
Doesn't matter for small tables.
For large tables, you want to add the column without a not null or default, commit that transaction, then populate the data, then add the not null and default constraint.
So in any case a lock is taken, but I've never seen anyone get too bent out of shape over that momentary mutual exclusion to deform the table's type (exception: in transactional DDL where cheap and expensive steps are inter-mingled).