Consumer producer implementation involving POSIX, semaphores, and locks
General note

It is very hard to tell if this program works or not: I don't know what is supposed to be a correct behaviour. I can only speculate that you didn't want to see a semaphore value ever to exceed 4. If it is the case, than it is a pure coincidence -- caused by the large delays (try to remove sleep()). Absolutely nothing in the code prevents two or more producers to "simultaneously" test the value, and proceed to post the semaphore.

Particular problems

There is no reason to serialize consumers' access to a sem_wait(). In a sense it defeats the purpose of the semaphore.

stdio is not thread safe. The fact that printouts haven't messed up is another coincidence, also caused by the sleep() delays.

The code creates a multitude of threads, but joins only two. Not a problem in a toy program; a bad resource leak in general.

A minor note: since the thread functions ignore the argument, you should pass NULL. Passing anything else (like (void *) (intptr_t) value) is misleading.