Innanzitutto un codice tipo:
buff.append(new String((byte[])data, "UTF-8"));
non è proprio correttissimo. Il punto è che in UTF-8 un carattere può essere rappresentato con più byte. Creare stringhe distinte da blocchetti di byte senza sapere a che punto eventualmente interrompono una sequenza multi-byte può essere un problema per il decoder interno. Anche se nel tuo caso specifico non ci fossero questi problemi, è comunque una cosa non molto bella e pulita.
Se il documento sai a priori che non è molto grosso, puoi "buttare" i byte a blocchi dentro un ByteArrayOutputStream, poi alla fine crei un String con tutto l'array di byte indicando il charset che ovviamente deve essere coerente con il contenuto del documento.
Altrimenti, vale in generale e sarebbe anche meglio, incapsulare il zin in un InputStreamReader (specificando charset), poi leggere a blocchi di char e buttarli nel StringBuffer.
Qui non centra la thread-safety ... il tuo StringBuffer è "locale", non è condiviso tra thread! Quindi non centrano questioni con i thread.