Det er længe siden, jeg postede dette spørgsmål, og jeg vil gerne sende et svar, der beskriver det nøjagtige scenarie, der førte til denne vanskelige NullPointerException
.
Jeg tror, at dette kan hjælpe fremtidige læsere, der støder på sådan en forvirrende undtagelse, til at tænke ud af boksen, da jeg havde næsten al mulig grund til at antage, at dette var en mysql-forbindelsesfejl, selvom det alligevel ikke var det.
Mens jeg undersøgte denne undtagelse, var jeg sikker på, at min applikation umuligt kunne lukke DB-forbindelsen, mens jeg forsøgte at læse data fra den, da mine DB-forbindelser ikke deles på tværs af tråde, og hvis den samme tråd lukkede forbindelsen og derefter forsøgte at få adgang det, skulle en anden undtagelse være blevet kastet (noget SQLException
). Det var hovedårsagen til, at jeg havde mistanke om en mysql-forbindelsesfejl.
Det viste sig, at der var to tråde, der fik adgang til den samme forbindelse trods alt. Grunden til, at det var svært at finde ud af, var, at en af disse tråde var en garbage collector-tråd .
Går tilbage til koden, jeg har sendt:
Connection conn = ... // the connection is open
...
for (String someID : someIDs) {
SomeClass sc = null;
PreparedStatement
stmt = conn.prepareStatement ("SELECT A, B, C, D, E, F, G, H FROM T WHERE A = ?");
stmt.setString (1, "someID");
ResultSet res = stmt.executeQuery ();
if (res.next ()) {
sc = new SomeClass ();
sc.setA (res.getString (1));
sc.setB (res.getString (2));
sc.setC (res.getString (3));
sc.setD (res.getString (4));
sc.setE (res.getString (5));
sc.setF (res.getInt (6));
sc.setG (res.getString (7));
sc.setH (res.getByte (8)); // the exception is thrown here
}
stmt.close ();
conn.commit ();
if (sc != null) {
// do some processing that involves loading other records from the
// DB using the same connection
}
}
conn.close();
Problemet ligger i afsnittet "udfør noget behandling, der involverer indlæsning af andre poster fra DB'en ved hjælp af den samme forbindelse", som jeg desværre ikke inkluderede i mit oprindelige spørgsmål, da jeg ikke troede, problemet var der.
Zoomer vi ind på det afsnit, har vi:
if (sc != null) {
...
someMethod (conn);
...
}
Og someMethod
ser sådan ud:
public void someMethod (Connection conn)
{
...
SomeOtherClass instance = new SomeOtherClass (conn);
...
}
SomeOtherClass
ser sådan ud (selvfølgelig forenkler jeg her):
public class SomeOtherClass
{
Connection conn;
public SomeOtherClass (Connection conn)
{
this.conn = conn;
}
protected void finalize() throws Throwable
{
if (this.conn != null)
conn.close();
}
}
SomeOtherClass
kan oprette sin egen DB-forbindelse i nogle scenarier, men kan acceptere en eksisterende forbindelse i andre scenarier, som den vi har her.
Som du kan se, indeholder denne sektion et opkald til someMethod
der accepterer den åbne forbindelse som argument. someMethod
sender forbindelsen til en lokal forekomst af SomeOtherClass
. SomeOtherClass
havde en finalize
metode, der lukker forbindelsen.
Nu, efter someMethod
returnerer, instance
bliver berettiget til renovation. Når det samles affald, bliver det finalize
metode kaldes af garbage collector-tråden, som lukker forbindelsen.
Nu vender vi tilbage til for-løkken, som fortsætter med at udføre SELECT-sætninger ved hjælp af den samme forbindelse, som til enhver tid kan lukkes af garbage collector-tråden.
Hvis garbage collector-tråden tilfældigvis lukker forbindelsen, mens applikationstråden er midt i en eller anden mysql-forbindelsesmetode, der er afhængig af, at forbindelsen er åben, vises en NullPointerException
kan forekomme.
Fjerner finalize
metode løste problemet.
Vi tilsidesætter ikke ofte finalize
metode i vores klasser, hvilket gjorde det meget vanskeligt at lokalisere fejlen.