Q:

Why is ADD CONSTRAINT ... NOT VALID taking a long time? 

I added NOT NULL constraints to some columns like this, expecting the command to be very fast because of NOT VALID.  However, it took about 60-100 seconds, and seemed to lock the table for the whole time (based on other things failing during that time).  This is on a table with about 8,600,000 rows.  Why did this happen?  Would it have been better to do 4 commands instead of 1? 
Postgres 12
ALTER TABLE my_table
  ADD CONSTRAINT my_table_column1_not_null CHECK (column1 IS NOT NULL) NOT VALID,
  ADD CONSTRAINT my_table_column2_not_null CHECK (column2 IS NOT NULL) NOT VALID,
  ADD CONSTRAINT my_table_column3_not_null CHECK (column3 IS NOT NULL) NOT VALID,
  ADD CONSTRAINT my_table_column4_not_null CHECK (column4 IS NOT NULL) NOT VALID;

A:

The ALTER TABLE needs an "access exclusive" lock on the table.   While it will only hold that lock for microseconds once it does gets it, it still might block indefinitely while waiting to acquire it if any other session is already holding any lock on the table.   And once it does block then other sessions, even ones wanting a weaker lock, will block behind it.   (PostgreSQL does not allow queue jumping, so a strong lock which is waiting to get granted will block incoming weak locks, even if those incoming locks would be compatible with the lock already being held.) 
If you want the ALTER TABLE to either succeed or fail nearly instantly, set lock_timeout to a low value.   Trying to run them as separate statements would make the situation worse, not better.