synchronizedについての続報
今回エンジニアに説明するために使ったもの。
かなりいい加減だが、1億人に1人くらいの確立で役に立つなら50人以上に役に立つということではなかろうかと思いアップすることにした。
import java.util.List; import java.util.ArrayList; public class dao { public static void main(String[] args){ BaseThread[] threads = new BaseThread[] { new ThreadA(DaoFactory.getDaoA()), new ThreadA(DaoFactory.getDaoA()), new ThreadA(DaoFactory.getDaoA()), new ThreadA(DaoFactory.getDaoA()), new ThreadA(DaoFactory.getDaoB()), new ThreadA(DaoFactory.getDaoB()), new ThreadA(DaoFactory.getDaoB()), new ThreadA(DaoFactory.getDaoB()), new ThreadA(DaoFactory.getDaoC()), new ThreadA(DaoFactory.getDaoC()), new ThreadA(DaoFactory.getDaoC()), new ThreadA(DaoFactory.getDaoC()), }; for (BaseThread thread : threads) { thread.start(); } int result = 0; try{ for (BaseThread thread : threads) { thread.join(); } }catch(InterruptedException ie){ } } } abstract class AbstracJDBCDao<E extends BaseObject> { public abstract Database getDatabase(); //public synchronized int insert(E entity) { public int insert(E entity) { Database db = this.getDatabase(); List<BaseObject> table = db.getTable(); int maxId = 0; synchronized(entity.getClass()) { //synchronized(this.getClass()) { //synchronized(AbstracJDBCDao.class) { for (BaseObject base : table) { if (base.getIndex() > maxId) maxId = base.getIndex(); } try { Thread.sleep(50); } catch(Exception ex) { ex.printStackTrace(); } maxId++; entity.setIndex(maxId); db.addRecord(entity); } return maxId; } } class DaoA extends AbstracJDBCDao<Model> { public Database getDatabase() { return Database1.getInstance(); } } class DaoB extends AbstracJDBCDao<Model2> { public Database getDatabase() { return Database2.getInstance(); } } class DaoC extends AbstracJDBCDao<Model> { public Database getDatabase() { return Database1.getInstance(); } } class DaoFactory { public static DaoA getDaoA() { return new DaoA(); } public static DaoB getDaoB() { return new DaoB(); } public static DaoC getDaoC() { return new DaoC(); } } class BaseObject { private int index; public BaseObject() { this.index = 0; } public int getIndex() { return this.index; } public void setIndex(int value) { this.index = value; } } class Model extends BaseObject { } class Model2 extends BaseObject { } class Database { private List<BaseObject> table = new ArrayList<BaseObject>(); public List<BaseObject> getTable() { return table; } public void addRecord(BaseObject entity) { this.table.add(entity); } } class Database1 extends Database { private static Database instance = new Database(); public static Database getInstance() { return instance; } } class Database2 extends Database { private static Database instance = new Database(); public static Database getInstance() { return instance; } } abstract class BaseThread extends Thread{ protected AbstracJDBCDao dao; public void run(){ BaseObject a; if (dao instanceof DaoB) { a = new Model2(); } else { a = new Model(); } dao.insert(a); System.out.println(dao.getClass().getName()+":"+a.getIndex()); } } class ThreadA extends BaseThread{ public ThreadA(AbstracJDBCDao dao) { this.dao = dao; } }
今回のフレームワークは以下のようなルールがある。
- DaoはDaoFactoryを通じて、毎回インスタンスとして取得
- Daoには対応するModelクラスがある
これに対して、synchronizedを使ってやりたいのは「insert時に自動的にIDを更新する処理を、マルチスレッド環境化で安全に動作させる」というもの。
無論、DB上にトリガなどを作れればそれが一番安全だし早いが、それが出来ないという前提であればどんなロックオブジェクトを利用するのが適切か、という話。
サンプルはだいぶ適当だが、コメントアウトされた場所を適当にはずしていただければテストできる。
駆け出しのJAVA系Webプログラマであれば一度は学んでおく必要があるものなので、時間のあるときにでも試していただければ。
なお、例ではentity.getClassを正解としているが、複数のDAOが同じModelを参照することは通常ないので、this.getClassでも問題はない。
ちなみにThreadAの存在はまったく意味がないので、紛らわしい場合は削除してもらって問題ない。DatabaseとDatabase2は、まあ、手抜きの成れの果てということで・・。