Доступ к значениям из другого потока

Мой вопрос: Как получить доступ к значениям из другого потока?

У меня есть два файла .java, Main.java и TrackHands.java.

Main.java

/**
 * This is the main class, it is used to start the program. The only use of this
 * is to make everything more organized.
 */
package Kinect;

//import processing.core.PApplet;
/**
 * @author Tony Nguyen <[email protected]>
 *
 */
public class Main
{

    public static void main(String _args[])
    {  
        Thread trackHands = new Thread(new TrackHands());
        trackHands.start();
    }
}

TrackHands.java

/*
 * This uses the normal Java layout to track the user and prints out the coordinates of the left and right hand
 */
package Kinect;

import SimpleOpenNI.*;
import processing.core.PApplet;
import processing.core.PVector;

/**
 * @author Tony Nguyen <[email protected]>
 * @version 1.0
 */
public class TrackHands extends PApplet implements Runnable
{

    private int handLeftX, handLeftY = 0; // Holds the coordinates of the left hand
    SimpleOpenNI kinect = new SimpleOpenNI(this); // kinect object

    /**
     * Constructor Takes no parameters
     */
    public TrackHands()
    {
    }

    /**
     * run This will be executed when the thread starts
     */
    @Override
    public void run()
    {
        IntVector userList = new IntVector(); // Make a vector of ints to store the list of users        
        PVector leftHand = new PVector(); // Make a vector to store the left hand
        PVector convertedLeftHand = new PVector();

        kinect.enableDepth();
        kinect.enableUser(SimpleOpenNI.SKEL_PROFILE_ALL);
        kinect.setMirror(true);

        while (true)
        {
            kinect.update();

            kinect.getUsers(userList); // Write the list of detected users into the vector

            if (userList.size() > 0) // Checks if a user is found
            {
                int userId = userList.get(0); // Get first user

                if (kinect.isTrackingSkeleton(userId)) // If successfully calibrated
                {
                    kinect.getJointPositionSkeleton(userId,
                            SimpleOpenNI.SKEL_LEFT_HAND, leftHand); // Put the position of the left hand into that vector

                    kinect.convertRealWorldToProjective(leftHand,
                            convertedLeftHand);

                    this.handLeftX = round(convertedLeftHand.x);
                    this.handLeftY = round(convertedLeftHand.y);
                }
            }
        }

    }

    // User-tracking callbacks!
    public void onNewUser(int userId)
    {
        System.out.println("Start pose detection");
        kinect.startPoseDetection("Psi", userId);
    }

    public void onEndCalibration(int userId, boolean successful)
    {
        if (successful)
        {
            System.out.println("  User calibrated !!!");
            kinect.startTrackingSkeleton(userId);

        } else
        {
            System.out.println("  Failed to calibrate user !!!");
            kinect.startPoseDetection("Psi", userId);
        }
    }

    public void onStartPose(String pose, int userId)
    {
        System.out.println("Started pose for user");
        kinect.stopPoseDetection(userId);
        kinect.requestCalibrationSkeleton(userId, true);
    }
}

Я попытался использовать геттер и сеттер, чтобы получить значения из TrackHands.java в другой поток. Пробовал создавать объекты и передавать значения в качестве параметров, но тогда моя программа не будет использовать эти новые значения в методе run().


person iKaos    schedule 13.04.2013    source источник
comment
если вы погуглите учебник java.util.concurrent, вы получите несколько хороших указателей docs. oracle.com/javase/tutorial/essential/concurrency/ вам нужно как минимум синхронизированное ключевое слово вокруг общего объекта, иначе вы можете получить грязные чтения   -  person tgkprog    schedule 13.04.2013


Ответы (2)


Чтобы получить значения из TrackHands, используйте метод get, который обращается к переменной экземпляра, установленной в run().

class TrackHands {
    Object output;

    public void run() {
        while(true) {
            output = new Object();
        }
    }

    public Object getOutput() {
        return output;
    }
}

Передайте TrackHands в свой потребительский объект и используйте его для вызова метода get getOutput().

Передача значений немного сложнее, потому что вы можете вызвать состояние гонки. Попробуйте что-нибудь вроде этого

class TrackHands {
    Object input = null;
    public boolean setInput(Object input) {
        if(this.input == null) {
            this.input = input;
            return true;
        } else {
            return false;
        }
   }
}

Когда ваш метод run() использует input, установите для него значение null, чтобы другой поток мог передать другой ввод. Ваш поток-производитель будет использовать этот цикл для передачи ввода:

public void sendInput(TrackHands th, Object input) {
    boolean done = false;
    while(!done) {
        done = th.setInput(input);
    }
}

Это будет продолжать попытки передать input до тех пор, пока это не удастся.

setInput использует ключевое слово synchronized, так что только один поток может вызвать этот метод одновременно, иначе вы получите состояние гонки.

person Zim-Zam O'Pootertoot    schedule 13.04.2013
comment
следует использовать объекты в java.util.concurrent для передачи значений между потоками - person tgkprog; 13.04.2013
comment
Ребята большое спасибо за помощь! Значения, которые я хочу получить от TrackHands, часто обновляются, и мне нужны только самые новые значения (это координаты X и Y от ваших рук), поэтому, на мой взгляд, блокировки на самом деле не нужны. Или вы все равно рекомендуете мне их использовать? Строка «Вывод объекта;» во второй строке мне нужно создать объект Object? Или это только для демонстрации? Потому что я действительно не знаю, как присвоить объекту значения, чтобы я мог получить его из другого потока. - person iKaos; 14.04.2013
comment
Если вам нужен только последний ввод, тогда нет необходимости в блокировке, и вы можете сделать объект любым типом, который вы хотите - это было только для демонстрационных целей. - person Zim-Zam O'Pootertoot; 14.04.2013
comment
Не могли бы вы помочь мне еще немного? Единственное решение, которое я могу придумать, это создать другой класс с двумя целыми числами и конструктором, который принимает два параметра целых чисел и инициализирует их двумя целыми числами. Добавьте метод get в новый класс и вызовите метод get в классе, в котором я хочу использовать ввод. И создайте объект и передайте ему значения. Но я думаю, что гораздо более простого и логичного решения я не могу придумать. - person iKaos; 14.04.2013
comment
Другой вариант — использовать два метода get, по одному на int. Или использовать один метод get, который возвращает массив int/ararlist. - person Zim-Zam O'Pootertoot; 14.04.2013
comment
Должен ли я сделать эти методы получения в TrackHands или в новом классе (как я упоминал в своем предыдущем комментарии)? Потому что я пытался сделать метод get ранее в TrackHands, и он почему-то не даст мне правильное значение при вызове в другом классе. - person iKaos; 14.04.2013
comment
Похоже, вы используете поплавки/двойники, так что это ваша проблема - они не обновляются атомарно, поэтому ваш метод get может получить их в несогласованном состоянии. Вот класс AtomicFloat, который вы можете использовать > stackoverflow.com/questions/5505460/ - person Zim-Zam O'Pootertoot; 14.04.2013

Мой друг решил мою проблему.

Хочу поблагодарить всех за помощь!

Main.java

/**
 * This is the main class, it is used to start the program. The only use of this
 * is to make everything more organized.
 */
package Kinect;

//import processing.core.PApplet;
/**
 * @author Tony Nguyen <[email protected]>
 *
 */
public class Main
{

    public static void main(String _args[])
    {
//        PApplet.main(new String[]
//                {
//                    Sensor.class.getName()
//                });

        ValueStore valueStore = new ValueStore(); // ADDED THIS LINE
        Thread trackHands = new Thread(new TrackHands(valueStore)); // ADDED THIS LINE
        trackHands.start();
    }
}

TrackHands.java

/*
 * This uses the normal Java layout to track the user and prints out the coordinates of the left and right hand
 */
package Kinect;

import SimpleOpenNI.*;
import processing.core.PApplet;
import processing.core.PVector;

/**
 * @author Tony Nguyen <[email protected]>
 * @version 1.0
 */
public class TrackHands extends PApplet implements Runnable
{

    private int handLeftX, handLeftY, handRightX, handRightY = 0; // Holds the coordinates of the left hand
    SimpleOpenNI kinect = new SimpleOpenNI(this); // kinect object
    private ValueStore valuesStore; // ADDED THIS LINE

    /**
     * Constructor Takes no parameters
     */
    public TrackHands()
    {
    }

    public TrackHands(ValueStore valuesStore)
    {
        this.valuesStore = valuesStore;
    }

    /**
     * run This will be executed when the thread starts
     */
    @Override
    public void run()
    {
        IntVector userList = new IntVector(); // Make a vector of ints to store the list of users        
        PVector leftHand = new PVector(); // Make a vector to store the left hand
        PVector rightHand = new PVector(); // Make a vector to store the right hand
        PVector convertedLeftHand = new PVector(); // Make a vector to store the actual left hand
        PVector convertedRightHand = new PVector(); // Make a vector to store the actual right hand

        kinect.enableDepth();
        kinect.enableUser(SimpleOpenNI.SKEL_PROFILE_ALL);
        kinect.setMirror(true);

        while (true)
        {
            kinect.update();

            kinect.getUsers(userList); // Write the list of detected users into the vector

            if (userList.size() > 0) // Checks if a user is found
            {
                int userId = userList.get(0); // Get first user

                if (kinect.isTrackingSkeleton(userId)) // If successfully calibrated
                {
                    kinect.getJointPositionSkeleton(userId,
                            SimpleOpenNI.SKEL_LEFT_HAND, leftHand); // Put the position of the left hand into that vector

                    kinect.getJointPositionSkeleton(userId,
                            SimpleOpenNI.SKEL_RIGHT_HAND, rightHand); // Put the position of the left hand into that vector

                    kinect.convertRealWorldToProjective(leftHand,
                            convertedLeftHand);

                    kinect.convertRealWorldToProjective(rightHand,
                            convertedRightHand);

                    this.handLeftX = round(convertedLeftHand.x);
                    this.handLeftY = round(convertedLeftHand.y);
                    this.handRightX = round(convertedRightHand.x);
                    this.handRightY = round(convertedRightHand.y);

                    valuesStore.setHandValues(handLeftX, handLeftY, handRightX, handRightY); // ADDED THIS LINE
                }
            }
        }

    }

    // User-tracking callbacks!
    public void onNewUser(int userId)
    {
        System.out.println("Start pose detection");
        kinect.startPoseDetection("Psi", userId);
    }

    public void onEndCalibration(int userId, boolean successful)
    {
        if (successful)
        {
            System.out.println("  User calibrated !!!");
            kinect.startTrackingSkeleton(userId);

        } else
        {
            System.out.println("  Failed to calibrate user !!!");
            kinect.startPoseDetection("Psi", userId);
        }
    }

    public void onStartPose(String pose, int userId)
    {
        System.out.println("Started pose for user");
        kinect.stopPoseDetection(userId);
        kinect.requestCalibrationSkeleton(userId, true);
    }
}

Затем добавил класс для хранения значений, чтобы другой класс мог получить к нему доступ.

ValueStore.java

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Kinect;

/**
 *
 * @author Tony Nguyen <[email protected]>
 */
public class ValueStore
{

    private int leftX, leftY, rightX, rightY = 0;

    public void setHandValues(int leftX, int leftY, int rightX, int rightY)
    {
        this.leftX = leftX;
        this.leftY = leftY;
        this.rightX = rightX;
        this.rightY = rightY;
    }

    public int getLeftX()
    {
        return this.leftX;
    }
}
person iKaos    schedule 15.04.2013