Другое событие NullPointerException
возникает, когда объявляется массив объектов, а затем сразу же пытается разыменовать его внутри.
String[] phrases = new String[10];
String keyPhrase = "Bird";
for(String phrase : phrases) {
System.out.println(phrase.equals(keyPhrase));
}
Этот конкретный NPE можно избежать, если порядок сравнения отменяется ; а именно, использовать .equals
для гарантированного непустого объекта.
Все элементы внутри массива инициализируются их общим начальным значением ; для любого типа массива объектов, это означает, что все элементы null
.
Вы должны инициализировать элементы в массиве перед доступом или разыменованием их.
String[] phrases = new String[] {"The bird", "A bird", "My bird", "Bird"};
String keyPhrase = "Bird";
for(String phrase : phrases) {
System.out.println(phrase.equals(keyPhrase));
}
Вот реализация, которая использует lockfile и повторяет PID в него. Это служит защитой, если процесс уничтожается прежде, чем удалить pidfile:
LOCKFILE=/tmp/lock.txt
if [ -e ${LOCKFILE} ] && kill -0 `cat ${LOCKFILE}`; then
echo "already running"
exit
fi
# make sure the lockfile is removed when we exit and then claim it
trap "rm -f ${LOCKFILE}; exit" INT TERM EXIT
echo $ > ${LOCKFILE}
# do stuff
sleep 1000
rm -f ${LOCKFILE}
прием здесь kill -0
, который не поставляет сигнала, но просто проверяет, существует ли процесс с данным PID. Также вызов к trap
гарантирует, что lockfile удален, даже когда Ваш процесс уничтожается (кроме kill -9
).
Путь скопления является способом пойти. Думайте о том, что происходит, когда сценарий внезапно умирает. В случае скопления Вы просто освобождаете скопление, но это не проблема. Кроме того, обратите внимание, что злой прием должен взять скопление на самом сценарии.. но это, конечно, позволяет Вам работать на всех парах в проблемы разрешения.
Быстрый и грязный?
#!/bin/sh
if [ -f sometempfile ]
echo "Already running... will now terminate."
exit
else
touch sometempfile
fi
..do what you want here..
rm sometempfile
PID и lockfiles являются определенно самыми надежными. Когда Вы пытаетесь запустить программу, она может проверить на lockfile, который и если она существует, она может использовать ps
, чтобы видеть, работает ли процесс все еще. Если это не, сценарий может запуститься, обновив PID в lockfile к его собственному.
Используйте flock(1)
для создания эксклюзивной ограниченной по объему блокировки на дескрипторе файла. Таким образом, можно даже синхронизировать различные части сценария.
#!/bin/bash
(
# Wait for lock on /var/lock/.myscript.exclusivelock (fd 200) for 10 seconds
flock -x -w 10 200 || exit 1
# Do stuff
) 200>/var/lock/.myscript.exclusivelock
Это гарантирует, что код между (
и )
выполняется только одним процессом за один раз и что процесс doesn’t ожидает слишком долго блокировки.
Протест: эта конкретная команда является частью util-linux
. При выполнении операционной системы кроме Linux это может или не может быть доступно.
Существует обертка вокруг скопления (2), названный системный вызов, лишено воображения, скапливается (1). Это делает относительно легким надежно получить монопольные блокировки, не вызывая беспокойство об очистке и т.д. существуют примеры на страница справочника относительно того, как использовать его в сценарии оболочки.
Для создания блокировки надежной, Вам нужна атомарная операция. Многие вышеупомянутые предложения не являются атомарными. Предложенный lockfile (1) утилита выглядит многообещающим как упомянутая страница справочника, что его "стойкое к NFS". Если Ваша ОС не поддерживает lockfile (1), и Ваше решение должно работать над NFS, у Вас нет многих опций....
NFSv2 начинает две атомарных операции:
С NFSv3, создать вызов является также атомарным.
операции Каталога НЕ являются атомарными под NFSv2 и NFSv3 (отошлите к книге 'NFS, Проиллюстрированный' Brent Callaghan, ISBN 0-201-32570-5; Brent является ветераном NFS в Sun).
Знание этого, можно реализовать спин-блокировки для файлов и каталогов (в оболочке, не PHP):
блокируют текущий dir:
while ! ln -s . lock; do :; done
блокируют файл:
while ! ln -s ${f} ${f}.lock; do :; done
разблокировали текущий dir (предположение, рабочий процесс действительно получил блокировку):
mv lock deleteme && rm deleteme
разблокировали файл (предположение, рабочий процесс действительно получил блокировку):
mv ${f}.lock ${f}.deleteme && rm ${f}.deleteme
Удаляют, является также не атомарным, поэтому сначала переименовывание (который является атомарным), и затем удалять.
Для символьной ссылки и переименовывают вызовы, оба имен файлов должны находиться в той же файловой системе. Мое предложение: используйте только простые имена файлов (никакие пути) и поместите файл и блокировку в тот же каталог.
Некоторые unixes имеют lockfile
, который уже очень похож на упомянутый flock
.
Из страницы справочника:
lockfile может использоваться для создания одного или нескольких семафорных файлов. Если блокировка - файл не может создать все указанные файлы (в указанном порядке), это ожидает sleeptime (значения по умолчанию к 8) секунды и повторяет последний файл, который не успешно выполнялся. Можно определить количество повторений, чтобы сделать, пока отказ не возвращается. Если количество повторений-1 (значение по умолчанию, т.е.-r-1) lockfile повторит навсегда.
Создайте файл блокировки в известном месте, и проверка на существование на сценарии запускаются? Помещение PID в файле могло бы быть полезным, если чья-то попытка разыскать ошибочный экземпляр это предотвращает выполнение сценария.
Все подходы, которые проверяют существование «файлов блокировки», ошибочны.
Почему? Потому что нет способа проверить, существует ли файл и создать его в одном атомарном действии. Из-за этого; есть условие гонки: БУДЕТ делать ваши попытки разрыва взаимного исключения.
Вместо этого вам нужно использовать mkdir
. mkdir
создает каталог, если он еще не существует, и если он существует, он устанавливает код выхода. Что еще более важно, он делает все это в одном атомарном действии, что делает его идеальным для этого сценария.
if ! mkdir /tmp/myscript.lock 2>/dev/null; then
echo "Myscript is already running." >&2
exit 1
fi
Подробнее см. Превосходный BashFAQ: http://mywiki.wooledge.org/BashFAQ/045
Если вы хотите позаботиться об устаревших замках, подойдет fuser (1) . Единственным недостатком здесь является то, что операция занимает около секунды,
Я считаю, что для машины Debian хорошим решением будет пакет lockfile-progs
. procmail
также поставляется с инструментом lockfile
. Однако иногда я не могу ничего из этого сделать.
Вот мое решение, которое использует mkdir
для атомарности и файл PID для обнаружения устаревших блокировок. Этот код в настоящее время работает на Cygwin и работает хорошо.
Чтобы использовать его, просто вызовите exclusive_lock_require
, когда вам нужен эксклюзивный доступ к чему-либо. Необязательный параметр имени блокировки позволяет разделять блокировки между разными сценариями. Также есть две функции нижнего уровня ( exclusive_lock_try
и exclusive_lock_retry
), если вам понадобится что-то более сложное.
function exclusive_lock_try() # [lockname]
{
local LOCK_NAME="${1:-`basename $0`}"
LOCK_DIR="/tmp/.${LOCK_NAME}.lock"
local LOCK_PID_FILE="${LOCK_DIR}/${LOCK_NAME}.pid"
if [ -e "$LOCK_DIR" ]
then
local LOCK_PID="`cat "$LOCK_PID_FILE" 2> /dev/null`"
if [ ! -z "$LOCK_PID" ] && kill -0 "$LOCK_PID" 2> /dev/null
then
# locked by non-dead process
echo "\"$LOCK_NAME\" lock currently held by PID $LOCK_PID"
return 1
else
# orphaned lock, take it over
( echo $$ > "$LOCK_PID_FILE" ) 2> /dev/null && local LOCK_PID="$$"
fi
fi
if [ "`trap -p EXIT`" != "" ]
then
# already have an EXIT trap
echo "Cannot get lock, already have an EXIT trap"
return 1
fi
if [ "$LOCK_PID" != "$$" ] &&
! ( umask 077 && mkdir "$LOCK_DIR" && umask 177 && echo $$ > "$LOCK_PID_FILE" ) 2> /dev/null
then
local LOCK_PID="`cat "$LOCK_PID_FILE" 2> /dev/null`"
# unable to acquire lock, new process got in first
echo "\"$LOCK_NAME\" lock currently held by PID $LOCK_PID"
return 1
fi
trap "/bin/rm -rf \"$LOCK_DIR\"; exit;" EXIT
return 0 # got lock
}
function exclusive_lock_retry() # [lockname] [retries] [delay]
{
local LOCK_NAME="$1"
local MAX_TRIES="${2:-5}"
local DELAY="${3:-2}"
local TRIES=0
local LOCK_RETVAL
while [ "$TRIES" -lt "$MAX_TRIES" ]
do
if [ "$TRIES" -gt 0 ]
then
sleep "$DELAY"
fi
local TRIES=$(( $TRIES + 1 ))
if [ "$TRIES" -lt "$MAX_TRIES" ]
then
exclusive_lock_try "$LOCK_NAME" > /dev/null
else
exclusive_lock_try "$LOCK_NAME"
fi
LOCK_RETVAL="${PIPESTATUS[0]}"
if [ "$LOCK_RETVAL" -eq 0 ]
then
return 0
fi
done
return "$LOCK_RETVAL"
}
function exclusive_lock_require() # [lockname] [retries] [delay]
{
if ! exclusive_lock_retry "$@"
then
exit 1
fi
}
Вам нужна атомарная операция, например flock, иначе это в конечном итоге не удастся.
Но что делать, если стая отсутствует. Ну есть mkdir. Это тоже атомарная операция. Только один процесс приведет к успешному выполнению mkdir, все остальные завершатся ошибкой.
Итак, код такой:
if mkdir /var/lock/.myscript.exclusivelock
then
# do stuff
:
rmdir /var/lock/.myscript.exclusivelock
fi
Вам нужно позаботиться о устаревших блокировках, иначе после сбоя ваш скрипт больше не запустится.